From 9d346f1afa36f2cf95a91fe1cc711ac77804a843 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Sat, 1 Aug 2026 12:07:54 -0600 Subject: [PATCH] =?UTF-8?q?feat(phase-26):=20h=5Fseq=20family=20sweep=20?= =?UTF-8?q?=E2=80=94=202192=20member-matches=20banked=20via=20remap=5Fhseq?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ov_SC01_000/ov_SC01_000_after.c | 120 ++++++++++- src/ov_SC01_000/ov_SC01_000_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC01_000/ov_SC01_000_jr_80135D20.c | 129 +++++++++++- src/ov_SC01_000/ov_SC01_000_jr_80140608.c | 23 ++- src/ov_SC01_000/ov_SC01_000_jr_80154C24.c | 57 +++++- src/ov_SC01_000/ov_SC01_000_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC01_000/ov_SC01_000_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC01_001/ov_SC01_001_after.c | 120 ++++++++++- src/ov_SC01_001/ov_SC01_001_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC01_001/ov_SC01_001_jr_80135D20.c | 129 +++++++++++- src/ov_SC01_001/ov_SC01_001_jr_80140608.c | 23 ++- src/ov_SC01_001/ov_SC01_001_jr_80154C24.c | 57 +++++- src/ov_SC01_001/ov_SC01_001_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC01_001/ov_SC01_001_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC01_004/ov_SC01_004_after.c | 120 ++++++++++- src/ov_SC01_004/ov_SC01_004_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC01_004/ov_SC01_004_jr_80135D20.c | 129 +++++++++++- src/ov_SC01_004/ov_SC01_004_jr_80140608.c | 23 ++- src/ov_SC01_004/ov_SC01_004_jr_80154C24.c | 57 +++++- src/ov_SC01_004/ov_SC01_004_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC01_004/ov_SC01_004_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC01_005/ov_SC01_005_after.c | 120 ++++++++++- src/ov_SC01_005/ov_SC01_005_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC01_005/ov_SC01_005_jr_80135D20.c | 129 +++++++++++- src/ov_SC01_005/ov_SC01_005_jr_80140608.c | 23 ++- src/ov_SC01_005/ov_SC01_005_jr_80154C24.c | 57 +++++- src/ov_SC01_005/ov_SC01_005_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC01_005/ov_SC01_005_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC01_006/ov_SC01_006_after.c | 120 ++++++++++- src/ov_SC01_006/ov_SC01_006_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC01_006/ov_SC01_006_jr_80135D20.c | 129 +++++++++++- src/ov_SC01_006/ov_SC01_006_jr_80140608.c | 23 ++- src/ov_SC01_006/ov_SC01_006_jr_80154C24.c | 57 +++++- src/ov_SC01_006/ov_SC01_006_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC01_006/ov_SC01_006_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC01_008/ov_SC01_008_after.c | 120 ++++++++++- src/ov_SC01_008/ov_SC01_008_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC01_008/ov_SC01_008_jr_80135D20.c | 129 +++++++++++- src/ov_SC01_008/ov_SC01_008_jr_80140608.c | 23 ++- src/ov_SC01_008/ov_SC01_008_jr_80154C24.c | 57 +++++- src/ov_SC01_008/ov_SC01_008_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC01_008/ov_SC01_008_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC01_009/ov_SC01_009_after.c | 120 ++++++++++- src/ov_SC01_009/ov_SC01_009_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC01_009/ov_SC01_009_jr_80135D20.c | 129 +++++++++++- src/ov_SC01_009/ov_SC01_009_jr_80140608.c | 23 ++- src/ov_SC01_009/ov_SC01_009_jr_80154C24.c | 57 +++++- src/ov_SC01_009/ov_SC01_009_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC01_009/ov_SC01_009_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC01_074/ov_SC01_074_after.c | 120 ++++++++++- src/ov_SC01_074/ov_SC01_074_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC01_074/ov_SC01_074_jr_80135D20.c | 129 +++++++++++- src/ov_SC01_074/ov_SC01_074_jr_80140608.c | 23 ++- src/ov_SC01_074/ov_SC01_074_jr_80154C24.c | 57 +++++- src/ov_SC01_074/ov_SC01_074_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC01_074/ov_SC01_074_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC01_080/ov_SC01_080_after.c | 120 ++++++++++- src/ov_SC01_080/ov_SC01_080_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC01_080/ov_SC01_080_jr_80135D20.c | 129 +++++++++++- src/ov_SC01_080/ov_SC01_080_jr_80140608.c | 23 ++- src/ov_SC01_080/ov_SC01_080_jr_80154C24.c | 57 +++++- src/ov_SC01_080/ov_SC01_080_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC01_080/ov_SC01_080_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC01_084/ov_SC01_084_after.c | 120 ++++++++++- src/ov_SC01_084/ov_SC01_084_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC01_084/ov_SC01_084_jr_80135D20.c | 129 +++++++++++- src/ov_SC01_084/ov_SC01_084_jr_80140608.c | 23 ++- src/ov_SC01_084/ov_SC01_084_jr_80154C24.c | 57 +++++- src/ov_SC01_084/ov_SC01_084_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC01_084/ov_SC01_084_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_000/ov_SC02_000_after.c | 120 ++++++++++- src/ov_SC02_000/ov_SC02_000_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_000/ov_SC02_000_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_000/ov_SC02_000_jr_80140608.c | 23 ++- src/ov_SC02_000/ov_SC02_000_jr_80154C24.c | 57 +++++- src/ov_SC02_000/ov_SC02_000_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_000/ov_SC02_000_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_003/ov_SC02_003_after.c | 120 ++++++++++- src/ov_SC02_003/ov_SC02_003_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_003/ov_SC02_003_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_003/ov_SC02_003_jr_80140608.c | 23 ++- src/ov_SC02_003/ov_SC02_003_jr_80154C24.c | 57 +++++- src/ov_SC02_003/ov_SC02_003_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_003/ov_SC02_003_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_004/ov_SC02_004_after.c | 120 ++++++++++- src/ov_SC02_004/ov_SC02_004_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_004/ov_SC02_004_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_004/ov_SC02_004_jr_80140608.c | 23 ++- src/ov_SC02_004/ov_SC02_004_jr_80154C24.c | 57 +++++- src/ov_SC02_004/ov_SC02_004_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_004/ov_SC02_004_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_005/ov_SC02_005_after.c | 120 ++++++++++- src/ov_SC02_005/ov_SC02_005_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_005/ov_SC02_005_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_005/ov_SC02_005_jr_80140608.c | 23 ++- src/ov_SC02_005/ov_SC02_005_jr_80154C24.c | 57 +++++- src/ov_SC02_005/ov_SC02_005_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_005/ov_SC02_005_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_011/ov_SC02_011_after.c | 120 ++++++++++- src/ov_SC02_011/ov_SC02_011_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_011/ov_SC02_011_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_011/ov_SC02_011_jr_80140608.c | 23 ++- src/ov_SC02_011/ov_SC02_011_jr_80154C24.c | 57 +++++- src/ov_SC02_011/ov_SC02_011_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_011/ov_SC02_011_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_015/ov_SC02_015_after.c | 120 ++++++++++- src/ov_SC02_015/ov_SC02_015_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_015/ov_SC02_015_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_015/ov_SC02_015_jr_80140608.c | 23 ++- src/ov_SC02_015/ov_SC02_015_jr_80154C24.c | 57 +++++- src/ov_SC02_015/ov_SC02_015_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_015/ov_SC02_015_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_016/ov_SC02_016_after.c | 120 ++++++++++- src/ov_SC02_016/ov_SC02_016_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_016/ov_SC02_016_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_016/ov_SC02_016_jr_80140608.c | 23 ++- src/ov_SC02_016/ov_SC02_016_jr_80154C24.c | 57 +++++- src/ov_SC02_016/ov_SC02_016_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_016/ov_SC02_016_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_017/ov_SC02_017_after.c | 120 ++++++++++- src/ov_SC02_017/ov_SC02_017_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_017/ov_SC02_017_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_017/ov_SC02_017_jr_80140608.c | 23 ++- src/ov_SC02_017/ov_SC02_017_jr_80154C24.c | 57 +++++- src/ov_SC02_017/ov_SC02_017_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_017/ov_SC02_017_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_021/ov_SC02_021_after.c | 120 ++++++++++- src/ov_SC02_021/ov_SC02_021_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_021/ov_SC02_021_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_021/ov_SC02_021_jr_80140608.c | 23 ++- src/ov_SC02_021/ov_SC02_021_jr_80154C24.c | 57 +++++- src/ov_SC02_021/ov_SC02_021_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_021/ov_SC02_021_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_026/ov_SC02_026_after.c | 120 ++++++++++- src/ov_SC02_026/ov_SC02_026_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_026/ov_SC02_026_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_026/ov_SC02_026_jr_80140608.c | 23 ++- src/ov_SC02_026/ov_SC02_026_jr_80154C24.c | 57 +++++- src/ov_SC02_026/ov_SC02_026_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_026/ov_SC02_026_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_027/ov_SC02_027_after.c | 120 ++++++++++- src/ov_SC02_027/ov_SC02_027_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_027/ov_SC02_027_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_027/ov_SC02_027_jr_80140608.c | 23 ++- src/ov_SC02_027/ov_SC02_027_jr_80154C24.c | 57 +++++- src/ov_SC02_027/ov_SC02_027_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_027/ov_SC02_027_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_028/ov_SC02_028_after.c | 120 ++++++++++- src/ov_SC02_028/ov_SC02_028_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_028/ov_SC02_028_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_028/ov_SC02_028_jr_80140608.c | 23 ++- src/ov_SC02_028/ov_SC02_028_jr_80154C24.c | 57 +++++- src/ov_SC02_028/ov_SC02_028_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_028/ov_SC02_028_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_031/ov_SC02_031_after.c | 120 ++++++++++- src/ov_SC02_031/ov_SC02_031_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_031/ov_SC02_031_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_031/ov_SC02_031_jr_80140608.c | 23 ++- src/ov_SC02_031/ov_SC02_031_jr_80154C24.c | 57 +++++- src/ov_SC02_031/ov_SC02_031_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_031/ov_SC02_031_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_035/ov_SC02_035_after.c | 120 ++++++++++- src/ov_SC02_035/ov_SC02_035_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_035/ov_SC02_035_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_035/ov_SC02_035_jr_80140608.c | 23 ++- src/ov_SC02_035/ov_SC02_035_jr_80154C24.c | 57 +++++- src/ov_SC02_035/ov_SC02_035_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_035/ov_SC02_035_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_039/ov_SC02_039_after.c | 120 ++++++++++- src/ov_SC02_039/ov_SC02_039_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_039/ov_SC02_039_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_039/ov_SC02_039_jr_80140608.c | 23 ++- src/ov_SC02_039/ov_SC02_039_jr_80154C24.c | 57 +++++- src/ov_SC02_039/ov_SC02_039_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_039/ov_SC02_039_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC02_041/ov_SC02_041_after.c | 120 ++++++++++- src/ov_SC02_041/ov_SC02_041_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC02_041/ov_SC02_041_jr_80135D20.c | 129 +++++++++++- src/ov_SC02_041/ov_SC02_041_jr_80140608.c | 23 ++- src/ov_SC02_041/ov_SC02_041_jr_80154C24.c | 57 +++++- src/ov_SC02_041/ov_SC02_041_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC02_041/ov_SC02_041_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_001/ov_SC03_001_after.c | 120 ++++++++++- src/ov_SC03_001/ov_SC03_001_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_001/ov_SC03_001_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_001/ov_SC03_001_jr_80140608.c | 23 ++- src/ov_SC03_001/ov_SC03_001_jr_80154C24.c | 57 +++++- src/ov_SC03_001/ov_SC03_001_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_001/ov_SC03_001_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_002/ov_SC03_002_after.c | 120 ++++++++++- src/ov_SC03_002/ov_SC03_002_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_002/ov_SC03_002_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_002/ov_SC03_002_jr_80140608.c | 23 ++- src/ov_SC03_002/ov_SC03_002_jr_80154C24.c | 57 +++++- src/ov_SC03_002/ov_SC03_002_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_002/ov_SC03_002_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_003/ov_SC03_003_after.c | 120 ++++++++++- src/ov_SC03_003/ov_SC03_003_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_003/ov_SC03_003_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_003/ov_SC03_003_jr_80140608.c | 23 ++- src/ov_SC03_003/ov_SC03_003_jr_80154C24.c | 57 +++++- src/ov_SC03_003/ov_SC03_003_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_003/ov_SC03_003_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_006/ov_SC03_006_after.c | 120 ++++++++++- src/ov_SC03_006/ov_SC03_006_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_006/ov_SC03_006_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_006/ov_SC03_006_jr_80140608.c | 23 ++- src/ov_SC03_006/ov_SC03_006_jr_80154C24.c | 57 +++++- src/ov_SC03_006/ov_SC03_006_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_006/ov_SC03_006_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_007/ov_SC03_007_after.c | 120 ++++++++++- src/ov_SC03_007/ov_SC03_007_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_007/ov_SC03_007_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_007/ov_SC03_007_jr_80140608.c | 23 ++- src/ov_SC03_007/ov_SC03_007_jr_80154C24.c | 57 +++++- src/ov_SC03_007/ov_SC03_007_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_007/ov_SC03_007_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_010/ov_SC03_010_after.c | 120 ++++++++++- src/ov_SC03_010/ov_SC03_010_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_010/ov_SC03_010_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_010/ov_SC03_010_jr_80140608.c | 23 ++- src/ov_SC03_010/ov_SC03_010_jr_80154C24.c | 57 +++++- src/ov_SC03_010/ov_SC03_010_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_010/ov_SC03_010_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_011/ov_SC03_011_after.c | 120 ++++++++++- src/ov_SC03_011/ov_SC03_011_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_011/ov_SC03_011_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_011/ov_SC03_011_jr_80140608.c | 23 ++- src/ov_SC03_011/ov_SC03_011_jr_80154C24.c | 57 +++++- src/ov_SC03_011/ov_SC03_011_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_011/ov_SC03_011_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_012/ov_SC03_012_after.c | 120 ++++++++++- src/ov_SC03_012/ov_SC03_012_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_012/ov_SC03_012_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_012/ov_SC03_012_jr_80140608.c | 23 ++- src/ov_SC03_012/ov_SC03_012_jr_80154C24.c | 57 +++++- src/ov_SC03_012/ov_SC03_012_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_012/ov_SC03_012_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_013/ov_SC03_013_after.c | 120 ++++++++++- src/ov_SC03_013/ov_SC03_013_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_013/ov_SC03_013_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_013/ov_SC03_013_jr_80140608.c | 23 ++- src/ov_SC03_013/ov_SC03_013_jr_80154C24.c | 57 +++++- src/ov_SC03_013/ov_SC03_013_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_013/ov_SC03_013_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_014/ov_SC03_014_after.c | 120 ++++++++++- src/ov_SC03_014/ov_SC03_014_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_014/ov_SC03_014_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_014/ov_SC03_014_jr_80140608.c | 23 ++- src/ov_SC03_014/ov_SC03_014_jr_80154C24.c | 57 +++++- src/ov_SC03_014/ov_SC03_014_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_014/ov_SC03_014_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_015/ov_SC03_015_after.c | 120 ++++++++++- src/ov_SC03_015/ov_SC03_015_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_015/ov_SC03_015_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_015/ov_SC03_015_jr_80140608.c | 23 ++- src/ov_SC03_015/ov_SC03_015_jr_80154C24.c | 57 +++++- src/ov_SC03_015/ov_SC03_015_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_015/ov_SC03_015_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_023/ov_SC03_023_after.c | 120 ++++++++++- src/ov_SC03_023/ov_SC03_023_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_023/ov_SC03_023_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_023/ov_SC03_023_jr_80140608.c | 23 ++- src/ov_SC03_023/ov_SC03_023_jr_80154C24.c | 57 +++++- src/ov_SC03_023/ov_SC03_023_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_023/ov_SC03_023_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_024/ov_SC03_024_after.c | 120 ++++++++++- src/ov_SC03_024/ov_SC03_024_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_024/ov_SC03_024_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_024/ov_SC03_024_jr_80140608.c | 23 ++- src/ov_SC03_024/ov_SC03_024_jr_80154C24.c | 57 +++++- src/ov_SC03_024/ov_SC03_024_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_024/ov_SC03_024_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_028/ov_SC03_028_after.c | 120 ++++++++++- src/ov_SC03_028/ov_SC03_028_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_028/ov_SC03_028_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_028/ov_SC03_028_jr_80140608.c | 23 ++- src/ov_SC03_028/ov_SC03_028_jr_80154C24.c | 57 +++++- src/ov_SC03_028/ov_SC03_028_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_028/ov_SC03_028_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_029/ov_SC03_029_after.c | 120 ++++++++++- src/ov_SC03_029/ov_SC03_029_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_029/ov_SC03_029_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_029/ov_SC03_029_jr_80140608.c | 23 ++- src/ov_SC03_029/ov_SC03_029_jr_80154C24.c | 57 +++++- src/ov_SC03_029/ov_SC03_029_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_029/ov_SC03_029_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_030/ov_SC03_030_after.c | 120 ++++++++++- src/ov_SC03_030/ov_SC03_030_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_030/ov_SC03_030_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_030/ov_SC03_030_jr_80140608.c | 23 ++- src/ov_SC03_030/ov_SC03_030_jr_80154C24.c | 57 +++++- src/ov_SC03_030/ov_SC03_030_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_030/ov_SC03_030_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_031/ov_SC03_031_after.c | 120 ++++++++++- src/ov_SC03_031/ov_SC03_031_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_031/ov_SC03_031_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_031/ov_SC03_031_jr_80140608.c | 23 ++- src/ov_SC03_031/ov_SC03_031_jr_80154C24.c | 57 +++++- src/ov_SC03_031/ov_SC03_031_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_031/ov_SC03_031_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_089/ov_SC03_089_after.c | 120 ++++++++++- src/ov_SC03_089/ov_SC03_089_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_089/ov_SC03_089_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_089/ov_SC03_089_jr_80140608.c | 23 ++- src/ov_SC03_089/ov_SC03_089_jr_80154C24.c | 57 +++++- src/ov_SC03_089/ov_SC03_089_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_089/ov_SC03_089_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_090/ov_SC03_090_after.c | 120 ++++++++++- src/ov_SC03_090/ov_SC03_090_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_090/ov_SC03_090_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_090/ov_SC03_090_jr_80140608.c | 23 ++- src/ov_SC03_090/ov_SC03_090_jr_80154C24.c | 57 +++++- src/ov_SC03_090/ov_SC03_090_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_090/ov_SC03_090_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_091/ov_SC03_091_after.c | 120 ++++++++++- src/ov_SC03_091/ov_SC03_091_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_091/ov_SC03_091_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_091/ov_SC03_091_jr_80140608.c | 23 ++- src/ov_SC03_091/ov_SC03_091_jr_80154C24.c | 57 +++++- src/ov_SC03_091/ov_SC03_091_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_091/ov_SC03_091_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_092/ov_SC03_092_after.c | 120 ++++++++++- src/ov_SC03_092/ov_SC03_092_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_092/ov_SC03_092_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_092/ov_SC03_092_jr_80140608.c | 23 ++- src/ov_SC03_092/ov_SC03_092_jr_80154C24.c | 57 +++++- src/ov_SC03_092/ov_SC03_092_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_092/ov_SC03_092_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_093/ov_SC03_093_after.c | 120 ++++++++++- src/ov_SC03_093/ov_SC03_093_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_093/ov_SC03_093_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_093/ov_SC03_093_jr_80140608.c | 23 ++- src/ov_SC03_093/ov_SC03_093_jr_80154C24.c | 57 +++++- src/ov_SC03_093/ov_SC03_093_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_093/ov_SC03_093_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_094/ov_SC03_094_after.c | 120 ++++++++++- src/ov_SC03_094/ov_SC03_094_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_094/ov_SC03_094_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_094/ov_SC03_094_jr_80140608.c | 23 ++- src/ov_SC03_094/ov_SC03_094_jr_80154C24.c | 57 +++++- src/ov_SC03_094/ov_SC03_094_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_094/ov_SC03_094_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_095/ov_SC03_095_after.c | 120 ++++++++++- src/ov_SC03_095/ov_SC03_095_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_095/ov_SC03_095_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_095/ov_SC03_095_jr_80140608.c | 23 ++- src/ov_SC03_095/ov_SC03_095_jr_80154C24.c | 57 +++++- src/ov_SC03_095/ov_SC03_095_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_095/ov_SC03_095_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_096/ov_SC03_096_after.c | 120 ++++++++++- src/ov_SC03_096/ov_SC03_096_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_096/ov_SC03_096_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_096/ov_SC03_096_jr_80140608.c | 23 ++- src/ov_SC03_096/ov_SC03_096_jr_80154C24.c | 57 +++++- src/ov_SC03_096/ov_SC03_096_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_096/ov_SC03_096_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_097/ov_SC03_097_after.c | 120 ++++++++++- src/ov_SC03_097/ov_SC03_097_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_097/ov_SC03_097_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_097/ov_SC03_097_jr_80140608.c | 23 ++- src/ov_SC03_097/ov_SC03_097_jr_80154C24.c | 57 +++++- src/ov_SC03_097/ov_SC03_097_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_097/ov_SC03_097_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_098/ov_SC03_098_after.c | 120 ++++++++++- src/ov_SC03_098/ov_SC03_098_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_098/ov_SC03_098_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_098/ov_SC03_098_jr_80140608.c | 23 ++- src/ov_SC03_098/ov_SC03_098_jr_80154C24.c | 57 +++++- src/ov_SC03_098/ov_SC03_098_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_098/ov_SC03_098_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_099/ov_SC03_099_after.c | 120 ++++++++++- src/ov_SC03_099/ov_SC03_099_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_099/ov_SC03_099_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_099/ov_SC03_099_jr_80140608.c | 23 ++- src/ov_SC03_099/ov_SC03_099_jr_80154C24.c | 57 +++++- src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_099/ov_SC03_099_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_100/ov_SC03_100_after.c | 120 ++++++++++- src/ov_SC03_100/ov_SC03_100_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_100/ov_SC03_100_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_100/ov_SC03_100_jr_80140608.c | 23 ++- src/ov_SC03_100/ov_SC03_100_jr_80154C24.c | 57 +++++- src/ov_SC03_100/ov_SC03_100_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_100/ov_SC03_100_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_101/ov_SC03_101_after.c | 120 ++++++++++- src/ov_SC03_101/ov_SC03_101_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_101/ov_SC03_101_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_101/ov_SC03_101_jr_80140608.c | 23 ++- src/ov_SC03_101/ov_SC03_101_jr_80154C24.c | 57 +++++- src/ov_SC03_101/ov_SC03_101_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_101/ov_SC03_101_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_102/ov_SC03_102_after.c | 120 ++++++++++- src/ov_SC03_102/ov_SC03_102_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_102/ov_SC03_102_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_102/ov_SC03_102_jr_80140608.c | 23 ++- src/ov_SC03_102/ov_SC03_102_jr_80154C24.c | 57 +++++- src/ov_SC03_102/ov_SC03_102_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_102/ov_SC03_102_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_103/ov_SC03_103_after.c | 120 ++++++++++- src/ov_SC03_103/ov_SC03_103_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_103/ov_SC03_103_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_103/ov_SC03_103_jr_80140608.c | 23 ++- src/ov_SC03_103/ov_SC03_103_jr_80154C24.c | 57 +++++- src/ov_SC03_103/ov_SC03_103_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_103/ov_SC03_103_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_104/ov_SC03_104_after.c | 120 ++++++++++- src/ov_SC03_104/ov_SC03_104_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_104/ov_SC03_104_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_104/ov_SC03_104_jr_80140608.c | 23 ++- src/ov_SC03_104/ov_SC03_104_jr_80154C24.c | 57 +++++- src/ov_SC03_104/ov_SC03_104_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_104/ov_SC03_104_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_105/ov_SC03_105_after.c | 120 ++++++++++- src/ov_SC03_105/ov_SC03_105_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_105/ov_SC03_105_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_105/ov_SC03_105_jr_80140608.c | 23 ++- src/ov_SC03_105/ov_SC03_105_jr_80154C24.c | 57 +++++- src/ov_SC03_105/ov_SC03_105_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_105/ov_SC03_105_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_108/ov_SC03_108_after.c | 120 ++++++++++- src/ov_SC03_108/ov_SC03_108_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_108/ov_SC03_108_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_108/ov_SC03_108_jr_80140608.c | 23 ++- src/ov_SC03_108/ov_SC03_108_jr_80154C24.c | 57 +++++- src/ov_SC03_108/ov_SC03_108_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_108/ov_SC03_108_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_109/ov_SC03_109_after.c | 120 ++++++++++- src/ov_SC03_109/ov_SC03_109_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_109/ov_SC03_109_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_109/ov_SC03_109_jr_80140608.c | 23 ++- src/ov_SC03_109/ov_SC03_109_jr_80154C24.c | 57 +++++- src/ov_SC03_109/ov_SC03_109_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_109/ov_SC03_109_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_110/ov_SC03_110_after.c | 120 ++++++++++- src/ov_SC03_110/ov_SC03_110_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_110/ov_SC03_110_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_110/ov_SC03_110_jr_80140608.c | 23 ++- src/ov_SC03_110/ov_SC03_110_jr_80154C24.c | 57 +++++- src/ov_SC03_110/ov_SC03_110_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_110/ov_SC03_110_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_111/ov_SC03_111_after.c | 120 ++++++++++- src/ov_SC03_111/ov_SC03_111_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_111/ov_SC03_111_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_111/ov_SC03_111_jr_80140608.c | 23 ++- src/ov_SC03_111/ov_SC03_111_jr_80154C24.c | 57 +++++- src/ov_SC03_111/ov_SC03_111_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_111/ov_SC03_111_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_112/ov_SC03_112_after.c | 120 ++++++++++- src/ov_SC03_112/ov_SC03_112_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_112/ov_SC03_112_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_112/ov_SC03_112_jr_80140608.c | 23 ++- src/ov_SC03_112/ov_SC03_112_jr_80154C24.c | 57 +++++- src/ov_SC03_112/ov_SC03_112_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_112/ov_SC03_112_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_113/ov_SC03_113_after.c | 120 ++++++++++- src/ov_SC03_113/ov_SC03_113_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_113/ov_SC03_113_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_113/ov_SC03_113_jr_80140608.c | 23 ++- src/ov_SC03_113/ov_SC03_113_jr_80154C24.c | 57 +++++- src/ov_SC03_113/ov_SC03_113_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_113/ov_SC03_113_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_114/ov_SC03_114_after.c | 120 ++++++++++- src/ov_SC03_114/ov_SC03_114_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_114/ov_SC03_114_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_114/ov_SC03_114_jr_80140608.c | 23 ++- src/ov_SC03_114/ov_SC03_114_jr_80154C24.c | 57 +++++- src/ov_SC03_114/ov_SC03_114_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_114/ov_SC03_114_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_115/ov_SC03_115_after.c | 120 ++++++++++- src/ov_SC03_115/ov_SC03_115_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_115/ov_SC03_115_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_115/ov_SC03_115_jr_80140608.c | 23 ++- src/ov_SC03_115/ov_SC03_115_jr_80154C24.c | 57 +++++- src/ov_SC03_115/ov_SC03_115_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_115/ov_SC03_115_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_116/ov_SC03_116_after.c | 120 ++++++++++- src/ov_SC03_116/ov_SC03_116_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_116/ov_SC03_116_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_116/ov_SC03_116_jr_80140608.c | 23 ++- src/ov_SC03_116/ov_SC03_116_jr_80154C24.c | 57 +++++- src/ov_SC03_116/ov_SC03_116_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_116/ov_SC03_116_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_117/ov_SC03_117_after.c | 120 ++++++++++- src/ov_SC03_117/ov_SC03_117_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_117/ov_SC03_117_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_117/ov_SC03_117_jr_80140608.c | 23 ++- src/ov_SC03_117/ov_SC03_117_jr_80154C24.c | 57 +++++- src/ov_SC03_117/ov_SC03_117_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_117/ov_SC03_117_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_118/ov_SC03_118_after.c | 120 ++++++++++- src/ov_SC03_118/ov_SC03_118_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_118/ov_SC03_118_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_118/ov_SC03_118_jr_80140608.c | 23 ++- src/ov_SC03_118/ov_SC03_118_jr_80154C24.c | 57 +++++- src/ov_SC03_118/ov_SC03_118_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_118/ov_SC03_118_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_119/ov_SC03_119_after.c | 120 ++++++++++- src/ov_SC03_119/ov_SC03_119_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_119/ov_SC03_119_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_119/ov_SC03_119_jr_80140608.c | 23 ++- src/ov_SC03_119/ov_SC03_119_jr_80154C24.c | 57 +++++- src/ov_SC03_119/ov_SC03_119_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_119/ov_SC03_119_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_121/ov_SC03_121_after.c | 120 ++++++++++- src/ov_SC03_121/ov_SC03_121_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_121/ov_SC03_121_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_121/ov_SC03_121_jr_80140608.c | 23 ++- src/ov_SC03_121/ov_SC03_121_jr_80154C24.c | 57 +++++- src/ov_SC03_121/ov_SC03_121_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_121/ov_SC03_121_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_124/ov_SC03_124_after.c | 120 ++++++++++- src/ov_SC03_124/ov_SC03_124_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_124/ov_SC03_124_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_124/ov_SC03_124_jr_80140608.c | 23 ++- src/ov_SC03_124/ov_SC03_124_jr_80154C24.c | 57 +++++- src/ov_SC03_124/ov_SC03_124_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_124/ov_SC03_124_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_125/ov_SC03_125_after.c | 120 ++++++++++- src/ov_SC03_125/ov_SC03_125_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_125/ov_SC03_125_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_125/ov_SC03_125_jr_80140608.c | 23 ++- src/ov_SC03_125/ov_SC03_125_jr_80154C24.c | 57 +++++- src/ov_SC03_125/ov_SC03_125_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_125/ov_SC03_125_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC03_126/ov_SC03_126_after.c | 120 ++++++++++- src/ov_SC03_126/ov_SC03_126_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC03_126/ov_SC03_126_jr_80135D20.c | 129 +++++++++++- src/ov_SC03_126/ov_SC03_126_jr_80140608.c | 23 ++- src/ov_SC03_126/ov_SC03_126_jr_80154C24.c | 57 +++++- src/ov_SC03_126/ov_SC03_126_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC03_126/ov_SC03_126_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_000/ov_SC04_000_after.c | 120 ++++++++++- src/ov_SC04_000/ov_SC04_000_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_000/ov_SC04_000_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_000/ov_SC04_000_jr_80140608.c | 23 ++- src/ov_SC04_000/ov_SC04_000_jr_80154C24.c | 57 +++++- src/ov_SC04_000/ov_SC04_000_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_000/ov_SC04_000_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_002/ov_SC04_002_after.c | 120 ++++++++++- src/ov_SC04_002/ov_SC04_002_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_002/ov_SC04_002_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_002/ov_SC04_002_jr_80140608.c | 23 ++- src/ov_SC04_002/ov_SC04_002_jr_80154C24.c | 57 +++++- src/ov_SC04_002/ov_SC04_002_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_002/ov_SC04_002_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_003/ov_SC04_003_after.c | 120 ++++++++++- src/ov_SC04_003/ov_SC04_003_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_003/ov_SC04_003_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_003/ov_SC04_003_jr_80140608.c | 23 ++- src/ov_SC04_003/ov_SC04_003_jr_80154C24.c | 57 +++++- src/ov_SC04_003/ov_SC04_003_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_003/ov_SC04_003_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_004/ov_SC04_004_after.c | 120 ++++++++++- src/ov_SC04_004/ov_SC04_004_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_004/ov_SC04_004_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_004/ov_SC04_004_jr_80140608.c | 23 ++- src/ov_SC04_004/ov_SC04_004_jr_80154C24.c | 57 +++++- src/ov_SC04_004/ov_SC04_004_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_004/ov_SC04_004_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_005/ov_SC04_005_after.c | 120 ++++++++++- src/ov_SC04_005/ov_SC04_005_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_005/ov_SC04_005_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_005/ov_SC04_005_jr_80140608.c | 23 ++- src/ov_SC04_005/ov_SC04_005_jr_80154C24.c | 57 +++++- src/ov_SC04_005/ov_SC04_005_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_005/ov_SC04_005_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_006/ov_SC04_006_after.c | 120 ++++++++++- src/ov_SC04_006/ov_SC04_006_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_006/ov_SC04_006_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_006/ov_SC04_006_jr_80140608.c | 23 ++- src/ov_SC04_006/ov_SC04_006_jr_80154C24.c | 57 +++++- src/ov_SC04_006/ov_SC04_006_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_006/ov_SC04_006_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_007/ov_SC04_007_after.c | 120 ++++++++++- src/ov_SC04_007/ov_SC04_007_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_007/ov_SC04_007_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_007/ov_SC04_007_jr_80140608.c | 23 ++- src/ov_SC04_007/ov_SC04_007_jr_80154C24.c | 57 +++++- src/ov_SC04_007/ov_SC04_007_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_007/ov_SC04_007_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_008/ov_SC04_008_after.c | 120 ++++++++++- src/ov_SC04_008/ov_SC04_008_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_008/ov_SC04_008_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_008/ov_SC04_008_jr_80140608.c | 23 ++- src/ov_SC04_008/ov_SC04_008_jr_80154C24.c | 57 +++++- src/ov_SC04_008/ov_SC04_008_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_008/ov_SC04_008_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_009/ov_SC04_009_after.c | 120 ++++++++++- src/ov_SC04_009/ov_SC04_009_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_009/ov_SC04_009_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_009/ov_SC04_009_jr_80140608.c | 23 ++- src/ov_SC04_009/ov_SC04_009_jr_80154C24.c | 57 +++++- src/ov_SC04_009/ov_SC04_009_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_009/ov_SC04_009_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_010/ov_SC04_010_after.c | 120 ++++++++++- src/ov_SC04_010/ov_SC04_010_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_010/ov_SC04_010_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_010/ov_SC04_010_jr_80140608.c | 23 ++- src/ov_SC04_010/ov_SC04_010_jr_80154C24.c | 57 +++++- src/ov_SC04_010/ov_SC04_010_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_010/ov_SC04_010_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_011/ov_SC04_011_after.c | 120 ++++++++++- src/ov_SC04_011/ov_SC04_011_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_011/ov_SC04_011_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_011/ov_SC04_011_jr_80140608.c | 23 ++- src/ov_SC04_011/ov_SC04_011_jr_80154C24.c | 57 +++++- src/ov_SC04_011/ov_SC04_011_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_011/ov_SC04_011_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_012/ov_SC04_012_after.c | 120 ++++++++++- src/ov_SC04_012/ov_SC04_012_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_012/ov_SC04_012_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_012/ov_SC04_012_jr_80140608.c | 23 ++- src/ov_SC04_012/ov_SC04_012_jr_80154C24.c | 57 +++++- src/ov_SC04_012/ov_SC04_012_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_012/ov_SC04_012_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_015/ov_SC04_015_after.c | 120 ++++++++++- src/ov_SC04_015/ov_SC04_015_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_015/ov_SC04_015_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_015/ov_SC04_015_jr_80140608.c | 23 ++- src/ov_SC04_015/ov_SC04_015_jr_80154C24.c | 57 +++++- src/ov_SC04_015/ov_SC04_015_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_015/ov_SC04_015_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_016/ov_SC04_016_after.c | 120 ++++++++++- src/ov_SC04_016/ov_SC04_016_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_016/ov_SC04_016_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_016/ov_SC04_016_jr_80140608.c | 23 ++- src/ov_SC04_016/ov_SC04_016_jr_80154C24.c | 57 +++++- src/ov_SC04_016/ov_SC04_016_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_016/ov_SC04_016_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_018/ov_SC04_018_after.c | 120 ++++++++++- src/ov_SC04_018/ov_SC04_018_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_018/ov_SC04_018_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_018/ov_SC04_018_jr_80140608.c | 23 ++- src/ov_SC04_018/ov_SC04_018_jr_80154C24.c | 57 +++++- src/ov_SC04_018/ov_SC04_018_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_018/ov_SC04_018_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_019/ov_SC04_019_after.c | 120 ++++++++++- src/ov_SC04_019/ov_SC04_019_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_019/ov_SC04_019_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_019/ov_SC04_019_jr_80140608.c | 23 ++- src/ov_SC04_019/ov_SC04_019_jr_80154C24.c | 57 +++++- src/ov_SC04_019/ov_SC04_019_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_019/ov_SC04_019_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_020/ov_SC04_020_after.c | 120 ++++++++++- src/ov_SC04_020/ov_SC04_020_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_020/ov_SC04_020_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_020/ov_SC04_020_jr_80140608.c | 23 ++- src/ov_SC04_020/ov_SC04_020_jr_80154C24.c | 57 +++++- src/ov_SC04_020/ov_SC04_020_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_020/ov_SC04_020_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC04_021/ov_SC04_021_after.c | 120 ++++++++++- src/ov_SC04_021/ov_SC04_021_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC04_021/ov_SC04_021_jr_80135D20.c | 129 +++++++++++- src/ov_SC04_021/ov_SC04_021_jr_80140608.c | 23 ++- src/ov_SC04_021/ov_SC04_021_jr_80154C24.c | 57 +++++- src/ov_SC04_021/ov_SC04_021_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC04_021/ov_SC04_021_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_000/ov_SC05_000_after.c | 120 ++++++++++- src/ov_SC05_000/ov_SC05_000_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_000/ov_SC05_000_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_000/ov_SC05_000_jr_80140608.c | 23 ++- src/ov_SC05_000/ov_SC05_000_jr_80154C24.c | 57 +++++- src/ov_SC05_000/ov_SC05_000_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_000/ov_SC05_000_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_001/ov_SC05_001_after.c | 120 ++++++++++- src/ov_SC05_001/ov_SC05_001_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_001/ov_SC05_001_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_001/ov_SC05_001_jr_80140608.c | 23 ++- src/ov_SC05_001/ov_SC05_001_jr_80154C24.c | 57 +++++- src/ov_SC05_001/ov_SC05_001_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_001/ov_SC05_001_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_002/ov_SC05_002_after.c | 120 ++++++++++- src/ov_SC05_002/ov_SC05_002_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_002/ov_SC05_002_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_002/ov_SC05_002_jr_80140608.c | 23 ++- src/ov_SC05_002/ov_SC05_002_jr_80154C24.c | 57 +++++- src/ov_SC05_002/ov_SC05_002_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_002/ov_SC05_002_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_003/ov_SC05_003_after.c | 120 ++++++++++- src/ov_SC05_003/ov_SC05_003_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_003/ov_SC05_003_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_003/ov_SC05_003_jr_80140608.c | 23 ++- src/ov_SC05_003/ov_SC05_003_jr_80154C24.c | 57 +++++- src/ov_SC05_003/ov_SC05_003_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_003/ov_SC05_003_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_004/ov_SC05_004_after.c | 120 ++++++++++- src/ov_SC05_004/ov_SC05_004_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_004/ov_SC05_004_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_004/ov_SC05_004_jr_80140608.c | 23 ++- src/ov_SC05_004/ov_SC05_004_jr_80154C24.c | 57 +++++- src/ov_SC05_004/ov_SC05_004_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_004/ov_SC05_004_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_005/ov_SC05_005_after.c | 120 ++++++++++- src/ov_SC05_005/ov_SC05_005_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_005/ov_SC05_005_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_005/ov_SC05_005_jr_80140608.c | 23 ++- src/ov_SC05_005/ov_SC05_005_jr_80154C24.c | 57 +++++- src/ov_SC05_005/ov_SC05_005_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_005/ov_SC05_005_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_006/ov_SC05_006_after.c | 120 ++++++++++- src/ov_SC05_006/ov_SC05_006_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_006/ov_SC05_006_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_006/ov_SC05_006_jr_80140608.c | 23 ++- src/ov_SC05_006/ov_SC05_006_jr_80154C24.c | 57 +++++- src/ov_SC05_006/ov_SC05_006_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_006/ov_SC05_006_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_007/ov_SC05_007_after.c | 120 ++++++++++- src/ov_SC05_007/ov_SC05_007_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_007/ov_SC05_007_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_007/ov_SC05_007_jr_80140608.c | 23 ++- src/ov_SC05_007/ov_SC05_007_jr_80154C24.c | 57 +++++- src/ov_SC05_007/ov_SC05_007_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_007/ov_SC05_007_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_008/ov_SC05_008_after.c | 120 ++++++++++- src/ov_SC05_008/ov_SC05_008_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_008/ov_SC05_008_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_008/ov_SC05_008_jr_80140608.c | 23 ++- src/ov_SC05_008/ov_SC05_008_jr_80154C24.c | 57 +++++- src/ov_SC05_008/ov_SC05_008_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_008/ov_SC05_008_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_009/ov_SC05_009_after.c | 120 ++++++++++- src/ov_SC05_009/ov_SC05_009_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_009/ov_SC05_009_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_009/ov_SC05_009_jr_80140608.c | 23 ++- src/ov_SC05_009/ov_SC05_009_jr_80154C24.c | 57 +++++- src/ov_SC05_009/ov_SC05_009_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_009/ov_SC05_009_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_010/ov_SC05_010_after.c | 120 ++++++++++- src/ov_SC05_010/ov_SC05_010_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_010/ov_SC05_010_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_010/ov_SC05_010_jr_80140608.c | 23 ++- src/ov_SC05_010/ov_SC05_010_jr_80154C24.c | 57 +++++- src/ov_SC05_010/ov_SC05_010_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_010/ov_SC05_010_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_011/ov_SC05_011_after.c | 120 ++++++++++- src/ov_SC05_011/ov_SC05_011_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_011/ov_SC05_011_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_011/ov_SC05_011_jr_80140608.c | 23 ++- src/ov_SC05_011/ov_SC05_011_jr_80154C24.c | 57 +++++- src/ov_SC05_011/ov_SC05_011_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_011/ov_SC05_011_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_017/ov_SC05_017_after.c | 120 ++++++++++- src/ov_SC05_017/ov_SC05_017_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_017/ov_SC05_017_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_017/ov_SC05_017_jr_80140608.c | 23 ++- src/ov_SC05_017/ov_SC05_017_jr_80154C24.c | 57 +++++- src/ov_SC05_017/ov_SC05_017_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_017/ov_SC05_017_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_018/ov_SC05_018_after.c | 120 ++++++++++- src/ov_SC05_018/ov_SC05_018_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_018/ov_SC05_018_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_018/ov_SC05_018_jr_80140608.c | 23 ++- src/ov_SC05_018/ov_SC05_018_jr_80154C24.c | 57 +++++- src/ov_SC05_018/ov_SC05_018_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_018/ov_SC05_018_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC05_019/ov_SC05_019_after.c | 120 ++++++++++- src/ov_SC05_019/ov_SC05_019_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC05_019/ov_SC05_019_jr_80135D20.c | 129 +++++++++++- src/ov_SC05_019/ov_SC05_019_jr_80140608.c | 23 ++- src/ov_SC05_019/ov_SC05_019_jr_80154C24.c | 57 +++++- src/ov_SC05_019/ov_SC05_019_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC05_019/ov_SC05_019_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_000/ov_SC06_000_after.c | 120 ++++++++++- src/ov_SC06_000/ov_SC06_000_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_000/ov_SC06_000_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_000/ov_SC06_000_jr_80140608.c | 23 ++- src/ov_SC06_000/ov_SC06_000_jr_80154C24.c | 57 +++++- src/ov_SC06_000/ov_SC06_000_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_000/ov_SC06_000_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_006/ov_SC06_006_after.c | 120 ++++++++++- src/ov_SC06_006/ov_SC06_006_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_006/ov_SC06_006_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_006/ov_SC06_006_jr_80140608.c | 23 ++- src/ov_SC06_006/ov_SC06_006_jr_80154C24.c | 57 +++++- src/ov_SC06_006/ov_SC06_006_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_006/ov_SC06_006_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_008/ov_SC06_008_after.c | 120 ++++++++++- src/ov_SC06_008/ov_SC06_008_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_008/ov_SC06_008_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_008/ov_SC06_008_jr_80140608.c | 23 ++- src/ov_SC06_008/ov_SC06_008_jr_80154C24.c | 57 +++++- src/ov_SC06_008/ov_SC06_008_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_008/ov_SC06_008_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_010/ov_SC06_010_after.c | 120 ++++++++++- src/ov_SC06_010/ov_SC06_010_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_010/ov_SC06_010_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_010/ov_SC06_010_jr_80140608.c | 23 ++- src/ov_SC06_010/ov_SC06_010_jr_80154C24.c | 57 +++++- src/ov_SC06_010/ov_SC06_010_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_010/ov_SC06_010_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_011/ov_SC06_011_after.c | 120 ++++++++++- src/ov_SC06_011/ov_SC06_011_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_011/ov_SC06_011_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_011/ov_SC06_011_jr_80140608.c | 23 ++- src/ov_SC06_011/ov_SC06_011_jr_80154C24.c | 57 +++++- src/ov_SC06_011/ov_SC06_011_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_011/ov_SC06_011_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_013/ov_SC06_013_after.c | 120 ++++++++++- src/ov_SC06_013/ov_SC06_013_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_013/ov_SC06_013_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_013/ov_SC06_013_jr_80140608.c | 23 ++- src/ov_SC06_013/ov_SC06_013_jr_80154C24.c | 57 +++++- src/ov_SC06_013/ov_SC06_013_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_013/ov_SC06_013_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_014/ov_SC06_014_after.c | 120 ++++++++++- src/ov_SC06_014/ov_SC06_014_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_014/ov_SC06_014_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_014/ov_SC06_014_jr_80140608.c | 23 ++- src/ov_SC06_014/ov_SC06_014_jr_80154C24.c | 57 +++++- src/ov_SC06_014/ov_SC06_014_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_014/ov_SC06_014_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_015/ov_SC06_015_after.c | 120 ++++++++++- src/ov_SC06_015/ov_SC06_015_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_015/ov_SC06_015_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_015/ov_SC06_015_jr_80140608.c | 23 ++- src/ov_SC06_015/ov_SC06_015_jr_80154C24.c | 57 +++++- src/ov_SC06_015/ov_SC06_015_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_015/ov_SC06_015_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_016/ov_SC06_016_after.c | 120 ++++++++++- src/ov_SC06_016/ov_SC06_016_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_016/ov_SC06_016_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_016/ov_SC06_016_jr_80140608.c | 23 ++- src/ov_SC06_016/ov_SC06_016_jr_80154C24.c | 57 +++++- src/ov_SC06_016/ov_SC06_016_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_016/ov_SC06_016_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_018/ov_SC06_018_after.c | 120 ++++++++++- src/ov_SC06_018/ov_SC06_018_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_018/ov_SC06_018_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_018/ov_SC06_018_jr_80140608.c | 23 ++- src/ov_SC06_018/ov_SC06_018_jr_80154C24.c | 57 +++++- src/ov_SC06_018/ov_SC06_018_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_018/ov_SC06_018_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_020/ov_SC06_020_after.c | 120 ++++++++++- src/ov_SC06_020/ov_SC06_020_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_020/ov_SC06_020_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_020/ov_SC06_020_jr_80140608.c | 23 ++- src/ov_SC06_020/ov_SC06_020_jr_80154C24.c | 57 +++++- src/ov_SC06_020/ov_SC06_020_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_020/ov_SC06_020_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_022/ov_SC06_022_after.c | 120 ++++++++++- src/ov_SC06_022/ov_SC06_022_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_022/ov_SC06_022_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_022/ov_SC06_022_jr_80140608.c | 23 ++- src/ov_SC06_022/ov_SC06_022_jr_80154C24.c | 57 +++++- src/ov_SC06_022/ov_SC06_022_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_022/ov_SC06_022_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_024/ov_SC06_024_after.c | 120 ++++++++++- src/ov_SC06_024/ov_SC06_024_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_024/ov_SC06_024_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_024/ov_SC06_024_jr_80140608.c | 23 ++- src/ov_SC06_024/ov_SC06_024_jr_80154C24.c | 57 +++++- src/ov_SC06_024/ov_SC06_024_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_024/ov_SC06_024_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_025/ov_SC06_025_after.c | 120 ++++++++++- src/ov_SC06_025/ov_SC06_025_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_025/ov_SC06_025_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_025/ov_SC06_025_jr_80140608.c | 23 ++- src/ov_SC06_025/ov_SC06_025_jr_80154C24.c | 57 +++++- src/ov_SC06_025/ov_SC06_025_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_025/ov_SC06_025_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_027/ov_SC06_027_after.c | 120 ++++++++++- src/ov_SC06_027/ov_SC06_027_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_027/ov_SC06_027_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_027/ov_SC06_027_jr_80140608.c | 23 ++- src/ov_SC06_027/ov_SC06_027_jr_80154C24.c | 57 +++++- src/ov_SC06_027/ov_SC06_027_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_027/ov_SC06_027_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_029/ov_SC06_029_after.c | 120 ++++++++++- src/ov_SC06_029/ov_SC06_029_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_029/ov_SC06_029_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_029/ov_SC06_029_jr_80140608.c | 23 ++- src/ov_SC06_029/ov_SC06_029_jr_80154C24.c | 57 +++++- src/ov_SC06_029/ov_SC06_029_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_029/ov_SC06_029_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_030/ov_SC06_030_after.c | 120 ++++++++++- src/ov_SC06_030/ov_SC06_030_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_030/ov_SC06_030_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_030/ov_SC06_030_jr_80140608.c | 23 ++- src/ov_SC06_030/ov_SC06_030_jr_80154C24.c | 57 +++++- src/ov_SC06_030/ov_SC06_030_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_030/ov_SC06_030_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_032/ov_SC06_032_after.c | 120 ++++++++++- src/ov_SC06_032/ov_SC06_032_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_032/ov_SC06_032_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_032/ov_SC06_032_jr_80140608.c | 23 ++- src/ov_SC06_032/ov_SC06_032_jr_80154C24.c | 57 +++++- src/ov_SC06_032/ov_SC06_032_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_032/ov_SC06_032_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC06_033/ov_SC06_033_after.c | 120 ++++++++++- src/ov_SC06_033/ov_SC06_033_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC06_033/ov_SC06_033_jr_80135D20.c | 129 +++++++++++- src/ov_SC06_033/ov_SC06_033_jr_80140608.c | 23 ++- src/ov_SC06_033/ov_SC06_033_jr_80154C24.c | 57 +++++- src/ov_SC06_033/ov_SC06_033_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC06_033/ov_SC06_033_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC07_000/ov_SC07_000_after.c | 120 ++++++++++- src/ov_SC07_000/ov_SC07_000_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC07_000/ov_SC07_000_jr_80135D20.c | 129 +++++++++++- src/ov_SC07_000/ov_SC07_000_jr_80140608.c | 23 ++- src/ov_SC07_000/ov_SC07_000_jr_80154C24.c | 57 +++++- src/ov_SC07_000/ov_SC07_000_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC07_000/ov_SC07_000_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC07_001/ov_SC07_001_after.c | 120 ++++++++++- src/ov_SC07_001/ov_SC07_001_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC07_001/ov_SC07_001_jr_80135D20.c | 129 +++++++++++- src/ov_SC07_001/ov_SC07_001_jr_80140608.c | 23 ++- src/ov_SC07_001/ov_SC07_001_jr_80154C24.c | 57 +++++- src/ov_SC07_001/ov_SC07_001_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC07_001/ov_SC07_001_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC07_002/ov_SC07_002_after.c | 120 ++++++++++- src/ov_SC07_002/ov_SC07_002_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC07_002/ov_SC07_002_jr_80135D20.c | 129 +++++++++++- src/ov_SC07_002/ov_SC07_002_jr_80140608.c | 23 ++- src/ov_SC07_002/ov_SC07_002_jr_80154C24.c | 57 +++++- src/ov_SC07_002/ov_SC07_002_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC07_002/ov_SC07_002_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC07_006/ov_SC07_006.c | 137 ++++++++++++- src/ov_SC07_006/ov_SC07_006_jr_80131340.c | 100 ++++++++- src/ov_SC07_006/ov_SC07_006_jr_80135D20.c | 129 +++++++++++- src/ov_SC07_006/ov_SC07_006_jr_80140608.c | 144 ++++++++++++- src/ov_SC07_006/ov_SC07_006_jr_80154C24.c | 57 +++++- src/ov_SC07_006/ov_SC07_006_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC07_006/ov_SC07_006_jr_8016AE5C.c | 118 ++++++++++- src/ov_SC07_007/ov_SC07_007.c | 137 ++++++++++++- src/ov_SC07_007/ov_SC07_007_jr_80131340.c | 100 ++++++++- src/ov_SC07_007/ov_SC07_007_jr_80135D20.c | 129 +++++++++++- src/ov_SC07_007/ov_SC07_007_jr_80140608.c | 173 +++++++++++++++- src/ov_SC07_007/ov_SC07_007_jr_80154C24.c | 57 +++++- src/ov_SC07_007/ov_SC07_007_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC07_007/ov_SC07_007_jr_8016AE5C.c | 118 ++++++++++- src/ov_SC07_008/ov_SC07_008_after.c | 120 ++++++++++- src/ov_SC07_008/ov_SC07_008_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC07_008/ov_SC07_008_jr_80135D20.c | 129 +++++++++++- src/ov_SC07_008/ov_SC07_008_jr_80140608.c | 23 ++- src/ov_SC07_008/ov_SC07_008_jr_80154C24.c | 57 +++++- src/ov_SC07_008/ov_SC07_008_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC07_008/ov_SC07_008_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC07_009/ov_SC07_009_after.c | 120 ++++++++++- src/ov_SC07_009/ov_SC07_009_jr_8012ACE0.c | 237 +++++++++++++++++++++- src/ov_SC07_009/ov_SC07_009_jr_80135D20.c | 129 +++++++++++- src/ov_SC07_009/ov_SC07_009_jr_80140608.c | 23 ++- src/ov_SC07_009/ov_SC07_009_jr_80154C24.c | 57 +++++- src/ov_SC07_009/ov_SC07_009_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC07_009/ov_SC07_009_jr_8016AB6C.c | 118 ++++++++++- src/ov_SC07_010/ov_SC07_010.c | 137 ++++++++++++- src/ov_SC07_010/ov_SC07_010_jr_80131340.c | 100 ++++++++- src/ov_SC07_010/ov_SC07_010_jr_80135D20.c | 129 +++++++++++- src/ov_SC07_010/ov_SC07_010_jr_80140608.c | 168 ++++++++++++++- src/ov_SC07_010/ov_SC07_010_jr_80154C24.c | 57 +++++- src/ov_SC07_010/ov_SC07_010_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC07_010/ov_SC07_010_jr_8016AE5C.c | 118 ++++++++++- src/ov_SC07_011/ov_SC07_011.c | 137 ++++++++++++- src/ov_SC07_011/ov_SC07_011_jr_80131340.c | 100 ++++++++- src/ov_SC07_011/ov_SC07_011_jr_80135D20.c | 129 +++++++++++- src/ov_SC07_011/ov_SC07_011_jr_80140608.c | 173 +++++++++++++++- src/ov_SC07_011/ov_SC07_011_jr_80154C24.c | 57 +++++- src/ov_SC07_011/ov_SC07_011_jr_8015C32C.c | 148 +++++++++++++- src/ov_SC07_011/ov_SC07_011_jr_8016AE5C.c | 118 ++++++++++- 959 files changed, 111589 insertions(+), 2481 deletions(-) diff --git a/src/ov_SC01_000/ov_SC01_000_after.c b/src/ov_SC01_000/ov_SC01_000_after.c index ccdc14bbdd..40df2bac9a 100644 --- a/src/ov_SC01_000/ov_SC01_000_after.c +++ b/src/ov_SC01_000/ov_SC01_000_after.c @@ -2279,7 +2279,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2994,7 +3068,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC01_000/ov_SC01_000_jr_8012ACE0.c b/src/ov_SC01_000/ov_SC01_000_jr_8012ACE0.c index 8d094f278e..4c60d3ec33 100644 --- a/src/ov_SC01_000/ov_SC01_000_jr_8012ACE0.c +++ b/src/ov_SC01_000/ov_SC01_000_jr_8012ACE0.c @@ -994,7 +994,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1904,9 +2039,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC01_000/ov_SC01_000_jr_80135D20.c b/src/ov_SC01_000/ov_SC01_000_jr_80135D20.c index 10402fb926..b53680b5bb 100644 --- a/src/ov_SC01_000/ov_SC01_000_jr_80135D20.c +++ b/src/ov_SC01_000/ov_SC01_000_jr_80135D20.c @@ -1290,13 +1290,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC01_000/ov_SC01_000_jr_80140608.c b/src/ov_SC01_000/ov_SC01_000_jr_80140608.c index 9d30d48410..88b23e5c36 100644 --- a/src/ov_SC01_000/ov_SC01_000_jr_80140608.c +++ b/src/ov_SC01_000/ov_SC01_000_jr_80140608.c @@ -1807,7 +1807,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80180304[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80180304[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC01_000/ov_SC01_000_jr_80154C24.c b/src/ov_SC01_000/ov_SC01_000_jr_80154C24.c index 40e6e0d0e3..68eb254a2b 100644 --- a/src/ov_SC01_000/ov_SC01_000_jr_80154C24.c +++ b/src/ov_SC01_000/ov_SC01_000_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC01_000/ov_SC01_000_jr_8015C32C.c b/src/ov_SC01_000/ov_SC01_000_jr_8015C32C.c index 5503ae4b1f..c3b0fdc430 100644 --- a/src/ov_SC01_000/ov_SC01_000_jr_8015C32C.c +++ b/src/ov_SC01_000/ov_SC01_000_jr_8015C32C.c @@ -3290,7 +3290,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3576,7 +3627,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801816C0[]; + extern unsigned short D_801816C2[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801816C0[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801816C2[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3594,7 +3668,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801816F0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801816F0)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC01_000/ov_SC01_000_jr_8016AB6C.c b/src/ov_SC01_000/ov_SC01_000_jr_8016AB6C.c index 70324caa16..43bd6d926b 100644 --- a/src/ov_SC01_000/ov_SC01_000_jr_8016AB6C.c +++ b/src/ov_SC01_000/ov_SC01_000_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3885,7 +3883,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4249,7 +4303,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4299,7 +4374,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_000/nonmatchings/ov_SC01_000_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80182138[])(void); diff --git a/src/ov_SC01_001/ov_SC01_001_after.c b/src/ov_SC01_001/ov_SC01_001_after.c index 9e4f7c1d24..c5470cd296 100644 --- a/src/ov_SC01_001/ov_SC01_001_after.c +++ b/src/ov_SC01_001/ov_SC01_001_after.c @@ -2279,7 +2279,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2994,7 +3068,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC01_001/ov_SC01_001_jr_8012ACE0.c b/src/ov_SC01_001/ov_SC01_001_jr_8012ACE0.c index 5327f3200b..8a8fe5187d 100644 --- a/src/ov_SC01_001/ov_SC01_001_jr_8012ACE0.c +++ b/src/ov_SC01_001/ov_SC01_001_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC01_001/ov_SC01_001_jr_80135D20.c b/src/ov_SC01_001/ov_SC01_001_jr_80135D20.c index ba6b123e8c..435e38b4bc 100644 --- a/src/ov_SC01_001/ov_SC01_001_jr_80135D20.c +++ b/src/ov_SC01_001/ov_SC01_001_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC01_001/ov_SC01_001_jr_80140608.c b/src/ov_SC01_001/ov_SC01_001_jr_80140608.c index 9320287631..38e2fe7406 100644 --- a/src/ov_SC01_001/ov_SC01_001_jr_80140608.c +++ b/src/ov_SC01_001/ov_SC01_001_jr_80140608.c @@ -1827,7 +1827,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80184F54[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80184F54[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC01_001/ov_SC01_001_jr_80154C24.c b/src/ov_SC01_001/ov_SC01_001_jr_80154C24.c index 50d77484de..2a94ddea02 100644 --- a/src/ov_SC01_001/ov_SC01_001_jr_80154C24.c +++ b/src/ov_SC01_001/ov_SC01_001_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC01_001/ov_SC01_001_jr_8015C32C.c b/src/ov_SC01_001/ov_SC01_001_jr_8015C32C.c index b525dc2fb7..a1523a55a1 100644 --- a/src/ov_SC01_001/ov_SC01_001_jr_8015C32C.c +++ b/src/ov_SC01_001/ov_SC01_001_jr_8015C32C.c @@ -3287,7 +3287,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3573,7 +3624,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80186310[]; + extern unsigned short D_80186312[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80186310[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80186312[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3591,7 +3665,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80186340; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80186340)[param_2]); + } + } +} + // @class: struct @@ -5926,7 +6051,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC01_001/ov_SC01_001_jr_8016AB6C.c b/src/ov_SC01_001/ov_SC01_001_jr_8016AB6C.c index e3a0829033..77f64feb1c 100644 --- a/src/ov_SC01_001/ov_SC01_001_jr_8016AB6C.c +++ b/src/ov_SC01_001/ov_SC01_001_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4247,7 +4301,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4297,7 +4372,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_001/nonmatchings/ov_SC01_001_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80186D88[])(void); diff --git a/src/ov_SC01_004/ov_SC01_004_after.c b/src/ov_SC01_004/ov_SC01_004_after.c index e04fd68eb8..d06404889b 100644 --- a/src/ov_SC01_004/ov_SC01_004_after.c +++ b/src/ov_SC01_004/ov_SC01_004_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC01_004/ov_SC01_004_jr_8012ACE0.c b/src/ov_SC01_004/ov_SC01_004_jr_8012ACE0.c index fd76927672..9e0fb00aec 100644 --- a/src/ov_SC01_004/ov_SC01_004_jr_8012ACE0.c +++ b/src/ov_SC01_004/ov_SC01_004_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC01_004/ov_SC01_004_jr_80135D20.c b/src/ov_SC01_004/ov_SC01_004_jr_80135D20.c index 9a8ebb1e87..bd3da957b1 100644 --- a/src/ov_SC01_004/ov_SC01_004_jr_80135D20.c +++ b/src/ov_SC01_004/ov_SC01_004_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC01_004/ov_SC01_004_jr_80140608.c b/src/ov_SC01_004/ov_SC01_004_jr_80140608.c index 915873dc11..c8012968ca 100644 --- a/src/ov_SC01_004/ov_SC01_004_jr_80140608.c +++ b/src/ov_SC01_004/ov_SC01_004_jr_80140608.c @@ -1811,7 +1811,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80180918[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80180918[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC01_004/ov_SC01_004_jr_80154C24.c b/src/ov_SC01_004/ov_SC01_004_jr_80154C24.c index a378c25939..736f830f77 100644 --- a/src/ov_SC01_004/ov_SC01_004_jr_80154C24.c +++ b/src/ov_SC01_004/ov_SC01_004_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC01_004/ov_SC01_004_jr_8015C32C.c b/src/ov_SC01_004/ov_SC01_004_jr_8015C32C.c index 710873f5d4..b18f323a9e 100644 --- a/src/ov_SC01_004/ov_SC01_004_jr_8015C32C.c +++ b/src/ov_SC01_004/ov_SC01_004_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80181CD4[]; + extern unsigned short D_80181CD6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80181CD4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80181CD6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80181D04; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80181D04)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC01_004/ov_SC01_004_jr_8016AB6C.c b/src/ov_SC01_004/ov_SC01_004_jr_8016AB6C.c index cff23aa65a..a9f7f7c8ea 100644 --- a/src/ov_SC01_004/ov_SC01_004_jr_8016AB6C.c +++ b/src/ov_SC01_004/ov_SC01_004_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4247,7 +4301,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4297,7 +4372,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_004/nonmatchings/ov_SC01_004_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018274C[])(void); diff --git a/src/ov_SC01_005/ov_SC01_005_after.c b/src/ov_SC01_005/ov_SC01_005_after.c index 90153542cb..a9095557ae 100644 --- a/src/ov_SC01_005/ov_SC01_005_after.c +++ b/src/ov_SC01_005/ov_SC01_005_after.c @@ -2282,7 +2282,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2997,7 +3071,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC01_005/ov_SC01_005_jr_8012ACE0.c b/src/ov_SC01_005/ov_SC01_005_jr_8012ACE0.c index 58cce97ca2..81020ef085 100644 --- a/src/ov_SC01_005/ov_SC01_005_jr_8012ACE0.c +++ b/src/ov_SC01_005/ov_SC01_005_jr_8012ACE0.c @@ -978,7 +978,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1888,9 +2023,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC01_005/ov_SC01_005_jr_80135D20.c b/src/ov_SC01_005/ov_SC01_005_jr_80135D20.c index 18ee7e4630..af9d110e6c 100644 --- a/src/ov_SC01_005/ov_SC01_005_jr_80135D20.c +++ b/src/ov_SC01_005/ov_SC01_005_jr_80135D20.c @@ -1290,13 +1290,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC01_005/ov_SC01_005_jr_80140608.c b/src/ov_SC01_005/ov_SC01_005_jr_80140608.c index f8a0717ef6..92b925d249 100644 --- a/src/ov_SC01_005/ov_SC01_005_jr_80140608.c +++ b/src/ov_SC01_005/ov_SC01_005_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183AAC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183AAC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC01_005/ov_SC01_005_jr_80154C24.c b/src/ov_SC01_005/ov_SC01_005_jr_80154C24.c index 9f3ef86bfd..7c33ee8b63 100644 --- a/src/ov_SC01_005/ov_SC01_005_jr_80154C24.c +++ b/src/ov_SC01_005/ov_SC01_005_jr_80154C24.c @@ -1344,7 +1344,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC01_005/ov_SC01_005_jr_8015C32C.c b/src/ov_SC01_005/ov_SC01_005_jr_8015C32C.c index 635217b850..b44eed70ea 100644 --- a/src/ov_SC01_005/ov_SC01_005_jr_8015C32C.c +++ b/src/ov_SC01_005/ov_SC01_005_jr_8015C32C.c @@ -3290,7 +3290,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3576,7 +3627,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184E68[]; + extern unsigned short D_80184E6A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184E68[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184E6A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3594,7 +3668,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184E98; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184E98)[param_2]); + } + } +} + // @class: struct @@ -5929,7 +6054,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC01_005/ov_SC01_005_jr_8016AB6C.c b/src/ov_SC01_005/ov_SC01_005_jr_8016AB6C.c index 3e8c5a8cba..6cad1d3111 100644 --- a/src/ov_SC01_005/ov_SC01_005_jr_8016AB6C.c +++ b/src/ov_SC01_005/ov_SC01_005_jr_8016AB6C.c @@ -470,12 +470,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3884,7 +3882,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4247,7 +4301,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4297,7 +4372,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_005/nonmatchings/ov_SC01_005_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801858E0[])(void); diff --git a/src/ov_SC01_006/ov_SC01_006_after.c b/src/ov_SC01_006/ov_SC01_006_after.c index 5afd50a155..93a05f2482 100644 --- a/src/ov_SC01_006/ov_SC01_006_after.c +++ b/src/ov_SC01_006/ov_SC01_006_after.c @@ -2282,7 +2282,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2997,7 +3071,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC01_006/ov_SC01_006_jr_8012ACE0.c b/src/ov_SC01_006/ov_SC01_006_jr_8012ACE0.c index dc72a13d0b..70f971e8b5 100644 --- a/src/ov_SC01_006/ov_SC01_006_jr_8012ACE0.c +++ b/src/ov_SC01_006/ov_SC01_006_jr_8012ACE0.c @@ -978,7 +978,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1888,9 +2023,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC01_006/ov_SC01_006_jr_80135D20.c b/src/ov_SC01_006/ov_SC01_006_jr_80135D20.c index 7152d350b8..e93e04671f 100644 --- a/src/ov_SC01_006/ov_SC01_006_jr_80135D20.c +++ b/src/ov_SC01_006/ov_SC01_006_jr_80135D20.c @@ -1290,13 +1290,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC01_006/ov_SC01_006_jr_80140608.c b/src/ov_SC01_006/ov_SC01_006_jr_80140608.c index f4f690c7c8..f375dd9d84 100644 --- a/src/ov_SC01_006/ov_SC01_006_jr_80140608.c +++ b/src/ov_SC01_006/ov_SC01_006_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183AAC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183AAC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC01_006/ov_SC01_006_jr_80154C24.c b/src/ov_SC01_006/ov_SC01_006_jr_80154C24.c index c46577360b..38f206abc4 100644 --- a/src/ov_SC01_006/ov_SC01_006_jr_80154C24.c +++ b/src/ov_SC01_006/ov_SC01_006_jr_80154C24.c @@ -1344,7 +1344,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC01_006/ov_SC01_006_jr_8015C32C.c b/src/ov_SC01_006/ov_SC01_006_jr_8015C32C.c index ae419d1bbf..4eb027f998 100644 --- a/src/ov_SC01_006/ov_SC01_006_jr_8015C32C.c +++ b/src/ov_SC01_006/ov_SC01_006_jr_8015C32C.c @@ -3290,7 +3290,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3576,7 +3627,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184E68[]; + extern unsigned short D_80184E6A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184E68[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184E6A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3594,7 +3668,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184E98; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184E98)[param_2]); + } + } +} + // @class: struct @@ -5929,7 +6054,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC01_006/ov_SC01_006_jr_8016AB6C.c b/src/ov_SC01_006/ov_SC01_006_jr_8016AB6C.c index 5963c33b18..4c032703b2 100644 --- a/src/ov_SC01_006/ov_SC01_006_jr_8016AB6C.c +++ b/src/ov_SC01_006/ov_SC01_006_jr_8016AB6C.c @@ -470,12 +470,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3884,7 +3882,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4247,7 +4301,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4297,7 +4372,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_006/nonmatchings/ov_SC01_006_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801858E0[])(void); diff --git a/src/ov_SC01_008/ov_SC01_008_after.c b/src/ov_SC01_008/ov_SC01_008_after.c index 8342582f7a..9f76aed523 100644 --- a/src/ov_SC01_008/ov_SC01_008_after.c +++ b/src/ov_SC01_008/ov_SC01_008_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC01_008/ov_SC01_008_jr_8012ACE0.c b/src/ov_SC01_008/ov_SC01_008_jr_8012ACE0.c index ca6ab966d7..defa2aac75 100644 --- a/src/ov_SC01_008/ov_SC01_008_jr_8012ACE0.c +++ b/src/ov_SC01_008/ov_SC01_008_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC01_008/ov_SC01_008_jr_80135D20.c b/src/ov_SC01_008/ov_SC01_008_jr_80135D20.c index 8d9c8c43ef..cb7cc49496 100644 --- a/src/ov_SC01_008/ov_SC01_008_jr_80135D20.c +++ b/src/ov_SC01_008/ov_SC01_008_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC01_008/ov_SC01_008_jr_80140608.c b/src/ov_SC01_008/ov_SC01_008_jr_80140608.c index 4f563d620a..a9eacd6e2d 100644 --- a/src/ov_SC01_008/ov_SC01_008_jr_80140608.c +++ b/src/ov_SC01_008/ov_SC01_008_jr_80140608.c @@ -1811,7 +1811,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80180E8C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80180E8C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC01_008/ov_SC01_008_jr_80154C24.c b/src/ov_SC01_008/ov_SC01_008_jr_80154C24.c index f53bdbb8e2..1a3c4f6858 100644 --- a/src/ov_SC01_008/ov_SC01_008_jr_80154C24.c +++ b/src/ov_SC01_008/ov_SC01_008_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC01_008/ov_SC01_008_jr_8015C32C.c b/src/ov_SC01_008/ov_SC01_008_jr_8015C32C.c index c9d02f2d40..0376a13bc0 100644 --- a/src/ov_SC01_008/ov_SC01_008_jr_8015C32C.c +++ b/src/ov_SC01_008/ov_SC01_008_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80182248[]; + extern unsigned short D_8018224A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80182248[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018224A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80182278; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80182278)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC01_008/ov_SC01_008_jr_8016AB6C.c b/src/ov_SC01_008/ov_SC01_008_jr_8016AB6C.c index e10eac1cc1..4ac1b622c3 100644 --- a/src/ov_SC01_008/ov_SC01_008_jr_8016AB6C.c +++ b/src/ov_SC01_008/ov_SC01_008_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_008/nonmatchings/ov_SC01_008_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80182CC0[])(void); diff --git a/src/ov_SC01_009/ov_SC01_009_after.c b/src/ov_SC01_009/ov_SC01_009_after.c index f2b7713720..001ec660a5 100644 --- a/src/ov_SC01_009/ov_SC01_009_after.c +++ b/src/ov_SC01_009/ov_SC01_009_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC01_009/ov_SC01_009_jr_8012ACE0.c b/src/ov_SC01_009/ov_SC01_009_jr_8012ACE0.c index 6f95c5c6a2..9cf2c5a5c2 100644 --- a/src/ov_SC01_009/ov_SC01_009_jr_8012ACE0.c +++ b/src/ov_SC01_009/ov_SC01_009_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC01_009/ov_SC01_009_jr_80135D20.c b/src/ov_SC01_009/ov_SC01_009_jr_80135D20.c index 5022eda9f2..aa5a282e72 100644 --- a/src/ov_SC01_009/ov_SC01_009_jr_80135D20.c +++ b/src/ov_SC01_009/ov_SC01_009_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC01_009/ov_SC01_009_jr_80140608.c b/src/ov_SC01_009/ov_SC01_009_jr_80140608.c index b6955344f5..cfaf44cf9a 100644 --- a/src/ov_SC01_009/ov_SC01_009_jr_80140608.c +++ b/src/ov_SC01_009/ov_SC01_009_jr_80140608.c @@ -1811,7 +1811,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80184074[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80184074[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC01_009/ov_SC01_009_jr_80154C24.c b/src/ov_SC01_009/ov_SC01_009_jr_80154C24.c index 2d226000bc..796ed4c7bf 100644 --- a/src/ov_SC01_009/ov_SC01_009_jr_80154C24.c +++ b/src/ov_SC01_009/ov_SC01_009_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC01_009/ov_SC01_009_jr_8015C32C.c b/src/ov_SC01_009/ov_SC01_009_jr_8015C32C.c index 72cc685e2b..dc8265f7f3 100644 --- a/src/ov_SC01_009/ov_SC01_009_jr_8015C32C.c +++ b/src/ov_SC01_009/ov_SC01_009_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80185430[]; + extern unsigned short D_80185432[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80185430[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80185432[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80185460; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80185460)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC01_009/ov_SC01_009_jr_8016AB6C.c b/src/ov_SC01_009/ov_SC01_009_jr_8016AB6C.c index 6c30ba1850..bdffbff99f 100644 --- a/src/ov_SC01_009/ov_SC01_009_jr_8016AB6C.c +++ b/src/ov_SC01_009/ov_SC01_009_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_009/nonmatchings/ov_SC01_009_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80185EA8[])(void); diff --git a/src/ov_SC01_074/ov_SC01_074_after.c b/src/ov_SC01_074/ov_SC01_074_after.c index 1d99e783c8..be69f5cf96 100644 --- a/src/ov_SC01_074/ov_SC01_074_after.c +++ b/src/ov_SC01_074/ov_SC01_074_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC01_074/ov_SC01_074_jr_8012ACE0.c b/src/ov_SC01_074/ov_SC01_074_jr_8012ACE0.c index 5ef9f1ecf7..f9dbe16155 100644 --- a/src/ov_SC01_074/ov_SC01_074_jr_8012ACE0.c +++ b/src/ov_SC01_074/ov_SC01_074_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC01_074/ov_SC01_074_jr_80135D20.c b/src/ov_SC01_074/ov_SC01_074_jr_80135D20.c index e7d61af80e..c1c3de9f79 100644 --- a/src/ov_SC01_074/ov_SC01_074_jr_80135D20.c +++ b/src/ov_SC01_074/ov_SC01_074_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC01_074/ov_SC01_074_jr_80140608.c b/src/ov_SC01_074/ov_SC01_074_jr_80140608.c index ba52d625d1..58f6c28812 100644 --- a/src/ov_SC01_074/ov_SC01_074_jr_80140608.c +++ b/src/ov_SC01_074/ov_SC01_074_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017FCF4[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017FCF4[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC01_074/ov_SC01_074_jr_80154C24.c b/src/ov_SC01_074/ov_SC01_074_jr_80154C24.c index 7690901d40..b4069dcade 100644 --- a/src/ov_SC01_074/ov_SC01_074_jr_80154C24.c +++ b/src/ov_SC01_074/ov_SC01_074_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC01_074/ov_SC01_074_jr_8015C32C.c b/src/ov_SC01_074/ov_SC01_074_jr_8015C32C.c index 7e9d2ae03d..5a1697fedd 100644 --- a/src/ov_SC01_074/ov_SC01_074_jr_8015C32C.c +++ b/src/ov_SC01_074/ov_SC01_074_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801810B0[]; + extern unsigned short D_801810B2[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801810B0[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801810B2[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801810E0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801810E0)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC01_074/ov_SC01_074_jr_8016AB6C.c b/src/ov_SC01_074/ov_SC01_074_jr_8016AB6C.c index bf03bb66f6..73babd04e9 100644 --- a/src/ov_SC01_074/ov_SC01_074_jr_8016AB6C.c +++ b/src/ov_SC01_074/ov_SC01_074_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_074/nonmatchings/ov_SC01_074_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80181B28[])(void); diff --git a/src/ov_SC01_080/ov_SC01_080_after.c b/src/ov_SC01_080/ov_SC01_080_after.c index 203466e4fb..699d406292 100644 --- a/src/ov_SC01_080/ov_SC01_080_after.c +++ b/src/ov_SC01_080/ov_SC01_080_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC01_080/ov_SC01_080_jr_8012ACE0.c b/src/ov_SC01_080/ov_SC01_080_jr_8012ACE0.c index 3ce5d1f57b..5c0f44b757 100644 --- a/src/ov_SC01_080/ov_SC01_080_jr_8012ACE0.c +++ b/src/ov_SC01_080/ov_SC01_080_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC01_080/ov_SC01_080_jr_80135D20.c b/src/ov_SC01_080/ov_SC01_080_jr_80135D20.c index 025d6b848c..370ba64f57 100644 --- a/src/ov_SC01_080/ov_SC01_080_jr_80135D20.c +++ b/src/ov_SC01_080/ov_SC01_080_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC01_080/ov_SC01_080_jr_80140608.c b/src/ov_SC01_080/ov_SC01_080_jr_80140608.c index 863e377c5a..529d85ba00 100644 --- a/src/ov_SC01_080/ov_SC01_080_jr_80140608.c +++ b/src/ov_SC01_080/ov_SC01_080_jr_80140608.c @@ -1828,7 +1828,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80184C54[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80184C54[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC01_080/ov_SC01_080_jr_80154C24.c b/src/ov_SC01_080/ov_SC01_080_jr_80154C24.c index 1b9bbf00c8..ea71f928c2 100644 --- a/src/ov_SC01_080/ov_SC01_080_jr_80154C24.c +++ b/src/ov_SC01_080/ov_SC01_080_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC01_080/ov_SC01_080_jr_8015C32C.c b/src/ov_SC01_080/ov_SC01_080_jr_8015C32C.c index d5d13041e2..76261942b5 100644 --- a/src/ov_SC01_080/ov_SC01_080_jr_8015C32C.c +++ b/src/ov_SC01_080/ov_SC01_080_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80186010[]; + extern unsigned short D_80186012[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80186010[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80186012[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80186040; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80186040)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC01_080/ov_SC01_080_jr_8016AB6C.c b/src/ov_SC01_080/ov_SC01_080_jr_8016AB6C.c index f345243679..850e5c1051 100644 --- a/src/ov_SC01_080/ov_SC01_080_jr_8016AB6C.c +++ b/src/ov_SC01_080/ov_SC01_080_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_080/nonmatchings/ov_SC01_080_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80186A88[])(void); diff --git a/src/ov_SC01_084/ov_SC01_084_after.c b/src/ov_SC01_084/ov_SC01_084_after.c index 9a91c5b492..9c0018172c 100644 --- a/src/ov_SC01_084/ov_SC01_084_after.c +++ b/src/ov_SC01_084/ov_SC01_084_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC01_084/ov_SC01_084_jr_8012ACE0.c b/src/ov_SC01_084/ov_SC01_084_jr_8012ACE0.c index 31cc5c4c2b..3fed465d18 100644 --- a/src/ov_SC01_084/ov_SC01_084_jr_8012ACE0.c +++ b/src/ov_SC01_084/ov_SC01_084_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC01_084/ov_SC01_084_jr_80135D20.c b/src/ov_SC01_084/ov_SC01_084_jr_80135D20.c index 11c5efaf28..4969dea926 100644 --- a/src/ov_SC01_084/ov_SC01_084_jr_80135D20.c +++ b/src/ov_SC01_084/ov_SC01_084_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC01_084/ov_SC01_084_jr_80140608.c b/src/ov_SC01_084/ov_SC01_084_jr_80140608.c index ba34cfb21e..298345598e 100644 --- a/src/ov_SC01_084/ov_SC01_084_jr_80140608.c +++ b/src/ov_SC01_084/ov_SC01_084_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80187F48[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80187F48[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC01_084/ov_SC01_084_jr_80154C24.c b/src/ov_SC01_084/ov_SC01_084_jr_80154C24.c index 45260b4ac1..05c6d96235 100644 --- a/src/ov_SC01_084/ov_SC01_084_jr_80154C24.c +++ b/src/ov_SC01_084/ov_SC01_084_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC01_084/ov_SC01_084_jr_8015C32C.c b/src/ov_SC01_084/ov_SC01_084_jr_8015C32C.c index 70662cac54..5ecc57e188 100644 --- a/src/ov_SC01_084/ov_SC01_084_jr_8015C32C.c +++ b/src/ov_SC01_084/ov_SC01_084_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80189304[]; + extern unsigned short D_80189306[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80189304[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80189306[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80189334; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80189334)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC01_084/ov_SC01_084_jr_8016AB6C.c b/src/ov_SC01_084/ov_SC01_084_jr_8016AB6C.c index b334710a56..498e027132 100644 --- a/src/ov_SC01_084/ov_SC01_084_jr_8016AB6C.c +++ b/src/ov_SC01_084/ov_SC01_084_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3884,7 +3882,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4247,7 +4301,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4297,7 +4372,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC01_084/nonmatchings/ov_SC01_084_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80189D7C[])(void); diff --git a/src/ov_SC02_000/ov_SC02_000_after.c b/src/ov_SC02_000/ov_SC02_000_after.c index 90e5ce9257..d6a7e37e29 100644 --- a/src/ov_SC02_000/ov_SC02_000_after.c +++ b/src/ov_SC02_000/ov_SC02_000_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_000/ov_SC02_000_jr_8012ACE0.c b/src/ov_SC02_000/ov_SC02_000_jr_8012ACE0.c index 5e5a90cd1a..6c5338ea9f 100644 --- a/src/ov_SC02_000/ov_SC02_000_jr_8012ACE0.c +++ b/src/ov_SC02_000/ov_SC02_000_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_000/ov_SC02_000_jr_80135D20.c b/src/ov_SC02_000/ov_SC02_000_jr_80135D20.c index b808040aee..d9426182e5 100644 --- a/src/ov_SC02_000/ov_SC02_000_jr_80135D20.c +++ b/src/ov_SC02_000/ov_SC02_000_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_000/ov_SC02_000_jr_80140608.c b/src/ov_SC02_000/ov_SC02_000_jr_80140608.c index 9b901b4621..b397309b18 100644 --- a/src/ov_SC02_000/ov_SC02_000_jr_80140608.c +++ b/src/ov_SC02_000/ov_SC02_000_jr_80140608.c @@ -1827,7 +1827,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018C2F8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018C2F8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_000/ov_SC02_000_jr_80154C24.c b/src/ov_SC02_000/ov_SC02_000_jr_80154C24.c index 60b808328f..99a9649ccf 100644 --- a/src/ov_SC02_000/ov_SC02_000_jr_80154C24.c +++ b/src/ov_SC02_000/ov_SC02_000_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_000/ov_SC02_000_jr_8015C32C.c b/src/ov_SC02_000/ov_SC02_000_jr_8015C32C.c index a3c80fa3e7..aa8da1f396 100644 --- a/src/ov_SC02_000/ov_SC02_000_jr_8015C32C.c +++ b/src/ov_SC02_000/ov_SC02_000_jr_8015C32C.c @@ -3287,7 +3287,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3573,7 +3624,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018D6B4[]; + extern unsigned short D_8018D6B6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018D6B4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018D6B6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3591,7 +3665,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018D6E4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018D6E4)[param_2]); + } + } +} + // @class: struct @@ -5926,7 +6051,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_000/ov_SC02_000_jr_8016AB6C.c b/src/ov_SC02_000/ov_SC02_000_jr_8016AB6C.c index 671193be86..66516775d6 100644 --- a/src/ov_SC02_000/ov_SC02_000_jr_8016AB6C.c +++ b/src/ov_SC02_000/ov_SC02_000_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3881,7 +3879,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4244,7 +4298,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4294,7 +4369,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_000/nonmatchings/ov_SC02_000_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018E12C[])(void); diff --git a/src/ov_SC02_003/ov_SC02_003_after.c b/src/ov_SC02_003/ov_SC02_003_after.c index 978de76bb0..7395951418 100644 --- a/src/ov_SC02_003/ov_SC02_003_after.c +++ b/src/ov_SC02_003/ov_SC02_003_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_003/ov_SC02_003_jr_8012ACE0.c b/src/ov_SC02_003/ov_SC02_003_jr_8012ACE0.c index e15f41b203..2bf9d820a4 100644 --- a/src/ov_SC02_003/ov_SC02_003_jr_8012ACE0.c +++ b/src/ov_SC02_003/ov_SC02_003_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_003/ov_SC02_003_jr_80135D20.c b/src/ov_SC02_003/ov_SC02_003_jr_80135D20.c index 81411cb9fe..23737b8c00 100644 --- a/src/ov_SC02_003/ov_SC02_003_jr_80135D20.c +++ b/src/ov_SC02_003/ov_SC02_003_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_003/ov_SC02_003_jr_80140608.c b/src/ov_SC02_003/ov_SC02_003_jr_80140608.c index 5c1b6265fc..fe0131fc25 100644 --- a/src/ov_SC02_003/ov_SC02_003_jr_80140608.c +++ b/src/ov_SC02_003/ov_SC02_003_jr_80140608.c @@ -1827,7 +1827,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018C2F8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018C2F8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_003/ov_SC02_003_jr_80154C24.c b/src/ov_SC02_003/ov_SC02_003_jr_80154C24.c index fed1a5753d..16563eba3e 100644 --- a/src/ov_SC02_003/ov_SC02_003_jr_80154C24.c +++ b/src/ov_SC02_003/ov_SC02_003_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_003/ov_SC02_003_jr_8015C32C.c b/src/ov_SC02_003/ov_SC02_003_jr_8015C32C.c index c536c615c2..247fead8af 100644 --- a/src/ov_SC02_003/ov_SC02_003_jr_8015C32C.c +++ b/src/ov_SC02_003/ov_SC02_003_jr_8015C32C.c @@ -3287,7 +3287,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3573,7 +3624,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018D6B4[]; + extern unsigned short D_8018D6B6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018D6B4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018D6B6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3591,7 +3665,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018D6E4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018D6E4)[param_2]); + } + } +} + // @class: struct @@ -5926,7 +6051,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_003/ov_SC02_003_jr_8016AB6C.c b/src/ov_SC02_003/ov_SC02_003_jr_8016AB6C.c index b125ec9467..436ed85004 100644 --- a/src/ov_SC02_003/ov_SC02_003_jr_8016AB6C.c +++ b/src/ov_SC02_003/ov_SC02_003_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3881,7 +3879,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4244,7 +4298,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4294,7 +4369,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_003/nonmatchings/ov_SC02_003_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018E12C[])(void); diff --git a/src/ov_SC02_004/ov_SC02_004_after.c b/src/ov_SC02_004/ov_SC02_004_after.c index 13e32a7f58..51438a1964 100644 --- a/src/ov_SC02_004/ov_SC02_004_after.c +++ b/src/ov_SC02_004/ov_SC02_004_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_004/ov_SC02_004_jr_8012ACE0.c b/src/ov_SC02_004/ov_SC02_004_jr_8012ACE0.c index 6ce359a51c..1e7bf8b01f 100644 --- a/src/ov_SC02_004/ov_SC02_004_jr_8012ACE0.c +++ b/src/ov_SC02_004/ov_SC02_004_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_004/ov_SC02_004_jr_80135D20.c b/src/ov_SC02_004/ov_SC02_004_jr_80135D20.c index 84396629ae..cf55f05512 100644 --- a/src/ov_SC02_004/ov_SC02_004_jr_80135D20.c +++ b/src/ov_SC02_004/ov_SC02_004_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_004/ov_SC02_004_jr_80140608.c b/src/ov_SC02_004/ov_SC02_004_jr_80140608.c index 17d6f94a77..ee2e7b03b7 100644 --- a/src/ov_SC02_004/ov_SC02_004_jr_80140608.c +++ b/src/ov_SC02_004/ov_SC02_004_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017F3D8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017F3D8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_004/ov_SC02_004_jr_80154C24.c b/src/ov_SC02_004/ov_SC02_004_jr_80154C24.c index cfadd0d54a..f439ca2e1b 100644 --- a/src/ov_SC02_004/ov_SC02_004_jr_80154C24.c +++ b/src/ov_SC02_004/ov_SC02_004_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_004/ov_SC02_004_jr_8015C32C.c b/src/ov_SC02_004/ov_SC02_004_jr_8015C32C.c index d3c9392906..87c08827e2 100644 --- a/src/ov_SC02_004/ov_SC02_004_jr_8015C32C.c +++ b/src/ov_SC02_004/ov_SC02_004_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80180794[]; + extern unsigned short D_80180796[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80180794[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80180796[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801807C4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801807C4)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_004/ov_SC02_004_jr_8016AB6C.c b/src/ov_SC02_004/ov_SC02_004_jr_8016AB6C.c index 9013f669d1..386fd9bee8 100644 --- a/src/ov_SC02_004/ov_SC02_004_jr_8016AB6C.c +++ b/src/ov_SC02_004/ov_SC02_004_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_004/nonmatchings/ov_SC02_004_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018120C[])(void); diff --git a/src/ov_SC02_005/ov_SC02_005_after.c b/src/ov_SC02_005/ov_SC02_005_after.c index 5ac7a8cbd2..c329a7ef60 100644 --- a/src/ov_SC02_005/ov_SC02_005_after.c +++ b/src/ov_SC02_005/ov_SC02_005_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_005/ov_SC02_005_jr_8012ACE0.c b/src/ov_SC02_005/ov_SC02_005_jr_8012ACE0.c index 5fb6e686d0..16bfd8050b 100644 --- a/src/ov_SC02_005/ov_SC02_005_jr_8012ACE0.c +++ b/src/ov_SC02_005/ov_SC02_005_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_005/ov_SC02_005_jr_80135D20.c b/src/ov_SC02_005/ov_SC02_005_jr_80135D20.c index 26af53105f..e05297b9f0 100644 --- a/src/ov_SC02_005/ov_SC02_005_jr_80135D20.c +++ b/src/ov_SC02_005/ov_SC02_005_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_005/ov_SC02_005_jr_80140608.c b/src/ov_SC02_005/ov_SC02_005_jr_80140608.c index 1bac75bcb7..7c02cc0e68 100644 --- a/src/ov_SC02_005/ov_SC02_005_jr_80140608.c +++ b/src/ov_SC02_005/ov_SC02_005_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80191FDC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80191FDC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_005/ov_SC02_005_jr_80154C24.c b/src/ov_SC02_005/ov_SC02_005_jr_80154C24.c index 2829106a91..334174b0af 100644 --- a/src/ov_SC02_005/ov_SC02_005_jr_80154C24.c +++ b/src/ov_SC02_005/ov_SC02_005_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_005/ov_SC02_005_jr_8015C32C.c b/src/ov_SC02_005/ov_SC02_005_jr_8015C32C.c index 7515543915..ca96407c0f 100644 --- a/src/ov_SC02_005/ov_SC02_005_jr_8015C32C.c +++ b/src/ov_SC02_005/ov_SC02_005_jr_8015C32C.c @@ -3287,7 +3287,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3573,7 +3624,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80193398[]; + extern unsigned short D_8019339A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80193398[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8019339A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3591,7 +3665,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801933C8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801933C8)[param_2]); + } + } +} + // @class: struct @@ -5926,7 +6051,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_005/ov_SC02_005_jr_8016AB6C.c b/src/ov_SC02_005/ov_SC02_005_jr_8016AB6C.c index 70f855a9ae..0ac43b60ce 100644 --- a/src/ov_SC02_005/ov_SC02_005_jr_8016AB6C.c +++ b/src/ov_SC02_005/ov_SC02_005_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3885,7 +3883,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4248,7 +4302,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4298,7 +4373,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_005/nonmatchings/ov_SC02_005_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80193E10[])(void); diff --git a/src/ov_SC02_011/ov_SC02_011_after.c b/src/ov_SC02_011/ov_SC02_011_after.c index ea1f3b6842..6887347b34 100644 --- a/src/ov_SC02_011/ov_SC02_011_after.c +++ b/src/ov_SC02_011/ov_SC02_011_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_011/ov_SC02_011_jr_8012ACE0.c b/src/ov_SC02_011/ov_SC02_011_jr_8012ACE0.c index f3b3cba21e..a7da6d9c9a 100644 --- a/src/ov_SC02_011/ov_SC02_011_jr_8012ACE0.c +++ b/src/ov_SC02_011/ov_SC02_011_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_011/ov_SC02_011_jr_80135D20.c b/src/ov_SC02_011/ov_SC02_011_jr_80135D20.c index 8b8df5eb43..55c965346c 100644 --- a/src/ov_SC02_011/ov_SC02_011_jr_80135D20.c +++ b/src/ov_SC02_011/ov_SC02_011_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_011/ov_SC02_011_jr_80140608.c b/src/ov_SC02_011/ov_SC02_011_jr_80140608.c index 86e5d177f2..5546d44e99 100644 --- a/src/ov_SC02_011/ov_SC02_011_jr_80140608.c +++ b/src/ov_SC02_011/ov_SC02_011_jr_80140608.c @@ -1828,7 +1828,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8019242C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8019242C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_011/ov_SC02_011_jr_80154C24.c b/src/ov_SC02_011/ov_SC02_011_jr_80154C24.c index 9d9a0b58b9..2dc63bc5f1 100644 --- a/src/ov_SC02_011/ov_SC02_011_jr_80154C24.c +++ b/src/ov_SC02_011/ov_SC02_011_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_011/ov_SC02_011_jr_8015C32C.c b/src/ov_SC02_011/ov_SC02_011_jr_8015C32C.c index 8cd4f69245..6ef21bafce 100644 --- a/src/ov_SC02_011/ov_SC02_011_jr_8015C32C.c +++ b/src/ov_SC02_011/ov_SC02_011_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801937E8[]; + extern unsigned short D_801937EA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801937E8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801937EA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80193818; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80193818)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_011/ov_SC02_011_jr_8016AB6C.c b/src/ov_SC02_011/ov_SC02_011_jr_8016AB6C.c index 54d2208965..bba8f9be2e 100644 --- a/src/ov_SC02_011/ov_SC02_011_jr_8016AB6C.c +++ b/src/ov_SC02_011/ov_SC02_011_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_011/nonmatchings/ov_SC02_011_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80194260[])(void); diff --git a/src/ov_SC02_015/ov_SC02_015_after.c b/src/ov_SC02_015/ov_SC02_015_after.c index 4b9c9a3365..dca471700d 100644 --- a/src/ov_SC02_015/ov_SC02_015_after.c +++ b/src/ov_SC02_015/ov_SC02_015_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_015/ov_SC02_015_jr_8012ACE0.c b/src/ov_SC02_015/ov_SC02_015_jr_8012ACE0.c index c6a6902eff..c3ea77b041 100644 --- a/src/ov_SC02_015/ov_SC02_015_jr_8012ACE0.c +++ b/src/ov_SC02_015/ov_SC02_015_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_015/ov_SC02_015_jr_80135D20.c b/src/ov_SC02_015/ov_SC02_015_jr_80135D20.c index f91b53ae4a..8f0f92f682 100644 --- a/src/ov_SC02_015/ov_SC02_015_jr_80135D20.c +++ b/src/ov_SC02_015/ov_SC02_015_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_015/ov_SC02_015_jr_80140608.c b/src/ov_SC02_015/ov_SC02_015_jr_80140608.c index 6c05f5e384..611d9ae1fb 100644 --- a/src/ov_SC02_015/ov_SC02_015_jr_80140608.c +++ b/src/ov_SC02_015/ov_SC02_015_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017F940[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017F940[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_015/ov_SC02_015_jr_80154C24.c b/src/ov_SC02_015/ov_SC02_015_jr_80154C24.c index eb290ce29f..62bea43ad1 100644 --- a/src/ov_SC02_015/ov_SC02_015_jr_80154C24.c +++ b/src/ov_SC02_015/ov_SC02_015_jr_80154C24.c @@ -1341,7 +1341,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_015/ov_SC02_015_jr_8015C32C.c b/src/ov_SC02_015/ov_SC02_015_jr_8015C32C.c index d441b11c87..40466c98a4 100644 --- a/src/ov_SC02_015/ov_SC02_015_jr_8015C32C.c +++ b/src/ov_SC02_015/ov_SC02_015_jr_8015C32C.c @@ -3287,7 +3287,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3573,7 +3624,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80180CFC[]; + extern unsigned short D_80180CFE[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80180CFC[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80180CFE[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3591,7 +3665,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80180D2C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80180D2C)[param_2]); + } + } +} + // @class: struct @@ -5926,7 +6051,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_015/ov_SC02_015_jr_8016AB6C.c b/src/ov_SC02_015/ov_SC02_015_jr_8016AB6C.c index 2930db26ab..0b3c8a183c 100644 --- a/src/ov_SC02_015/ov_SC02_015_jr_8016AB6C.c +++ b/src/ov_SC02_015/ov_SC02_015_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3881,7 +3879,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4244,7 +4298,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4294,7 +4369,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_015/nonmatchings/ov_SC02_015_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80181774[])(void); diff --git a/src/ov_SC02_016/ov_SC02_016_after.c b/src/ov_SC02_016/ov_SC02_016_after.c index b8833432c6..7f11986be5 100644 --- a/src/ov_SC02_016/ov_SC02_016_after.c +++ b/src/ov_SC02_016/ov_SC02_016_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_016/ov_SC02_016_jr_8012ACE0.c b/src/ov_SC02_016/ov_SC02_016_jr_8012ACE0.c index ce552b4e1a..901948d67c 100644 --- a/src/ov_SC02_016/ov_SC02_016_jr_8012ACE0.c +++ b/src/ov_SC02_016/ov_SC02_016_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_016/ov_SC02_016_jr_80135D20.c b/src/ov_SC02_016/ov_SC02_016_jr_80135D20.c index 23d92bdcfe..b6257cb315 100644 --- a/src/ov_SC02_016/ov_SC02_016_jr_80135D20.c +++ b/src/ov_SC02_016/ov_SC02_016_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_016/ov_SC02_016_jr_80140608.c b/src/ov_SC02_016/ov_SC02_016_jr_80140608.c index f4a2ea7e05..dba4e8d2d1 100644 --- a/src/ov_SC02_016/ov_SC02_016_jr_80140608.c +++ b/src/ov_SC02_016/ov_SC02_016_jr_80140608.c @@ -1828,7 +1828,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80185B78[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80185B78[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_016/ov_SC02_016_jr_80154C24.c b/src/ov_SC02_016/ov_SC02_016_jr_80154C24.c index aae12391c9..fa27ccfbe5 100644 --- a/src/ov_SC02_016/ov_SC02_016_jr_80154C24.c +++ b/src/ov_SC02_016/ov_SC02_016_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_016/ov_SC02_016_jr_8015C32C.c b/src/ov_SC02_016/ov_SC02_016_jr_8015C32C.c index 947e814174..9283e0adc8 100644 --- a/src/ov_SC02_016/ov_SC02_016_jr_8015C32C.c +++ b/src/ov_SC02_016/ov_SC02_016_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80186F34[]; + extern unsigned short D_80186F36[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80186F34[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80186F36[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80186F64; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80186F64)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_016/ov_SC02_016_jr_8016AB6C.c b/src/ov_SC02_016/ov_SC02_016_jr_8016AB6C.c index b149541757..4c8ce88086 100644 --- a/src/ov_SC02_016/ov_SC02_016_jr_8016AB6C.c +++ b/src/ov_SC02_016/ov_SC02_016_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_016/nonmatchings/ov_SC02_016_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801879AC[])(void); diff --git a/src/ov_SC02_017/ov_SC02_017_after.c b/src/ov_SC02_017/ov_SC02_017_after.c index ee068caf1c..44f1c0564f 100644 --- a/src/ov_SC02_017/ov_SC02_017_after.c +++ b/src/ov_SC02_017/ov_SC02_017_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_017/ov_SC02_017_jr_8012ACE0.c b/src/ov_SC02_017/ov_SC02_017_jr_8012ACE0.c index c1c8acea3b..47f1258762 100644 --- a/src/ov_SC02_017/ov_SC02_017_jr_8012ACE0.c +++ b/src/ov_SC02_017/ov_SC02_017_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_017/ov_SC02_017_jr_80135D20.c b/src/ov_SC02_017/ov_SC02_017_jr_80135D20.c index 52d68badad..30188e28c7 100644 --- a/src/ov_SC02_017/ov_SC02_017_jr_80135D20.c +++ b/src/ov_SC02_017/ov_SC02_017_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_017/ov_SC02_017_jr_80140608.c b/src/ov_SC02_017/ov_SC02_017_jr_80140608.c index 89355885ca..8230adba17 100644 --- a/src/ov_SC02_017/ov_SC02_017_jr_80140608.c +++ b/src/ov_SC02_017/ov_SC02_017_jr_80140608.c @@ -1828,7 +1828,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018BBD0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018BBD0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_017/ov_SC02_017_jr_80154C24.c b/src/ov_SC02_017/ov_SC02_017_jr_80154C24.c index f38d49f116..4152ff06c1 100644 --- a/src/ov_SC02_017/ov_SC02_017_jr_80154C24.c +++ b/src/ov_SC02_017/ov_SC02_017_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_017/ov_SC02_017_jr_8015C32C.c b/src/ov_SC02_017/ov_SC02_017_jr_8015C32C.c index 466138ef50..8980f1d3fb 100644 --- a/src/ov_SC02_017/ov_SC02_017_jr_8015C32C.c +++ b/src/ov_SC02_017/ov_SC02_017_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018CF8C[]; + extern unsigned short D_8018CF8E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018CF8C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018CF8E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018CFBC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018CFBC)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_017/ov_SC02_017_jr_8016AB6C.c b/src/ov_SC02_017/ov_SC02_017_jr_8016AB6C.c index 2a932bd12a..350b158a0c 100644 --- a/src/ov_SC02_017/ov_SC02_017_jr_8016AB6C.c +++ b/src/ov_SC02_017/ov_SC02_017_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_017/nonmatchings/ov_SC02_017_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018DA04[])(void); diff --git a/src/ov_SC02_021/ov_SC02_021_after.c b/src/ov_SC02_021/ov_SC02_021_after.c index 447db926b3..50b4370319 100644 --- a/src/ov_SC02_021/ov_SC02_021_after.c +++ b/src/ov_SC02_021/ov_SC02_021_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_021/ov_SC02_021_jr_8012ACE0.c b/src/ov_SC02_021/ov_SC02_021_jr_8012ACE0.c index 3079a5ce1d..0bb6c55dd2 100644 --- a/src/ov_SC02_021/ov_SC02_021_jr_8012ACE0.c +++ b/src/ov_SC02_021/ov_SC02_021_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_021/ov_SC02_021_jr_80135D20.c b/src/ov_SC02_021/ov_SC02_021_jr_80135D20.c index a81247c8eb..ddc823ea52 100644 --- a/src/ov_SC02_021/ov_SC02_021_jr_80135D20.c +++ b/src/ov_SC02_021/ov_SC02_021_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_021/ov_SC02_021_jr_80140608.c b/src/ov_SC02_021/ov_SC02_021_jr_80140608.c index f09ad5a535..76c3ac7ad5 100644 --- a/src/ov_SC02_021/ov_SC02_021_jr_80140608.c +++ b/src/ov_SC02_021/ov_SC02_021_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801811C4[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801811C4[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_021/ov_SC02_021_jr_80154C24.c b/src/ov_SC02_021/ov_SC02_021_jr_80154C24.c index 9ce2ba7bec..8a6bfc4db3 100644 --- a/src/ov_SC02_021/ov_SC02_021_jr_80154C24.c +++ b/src/ov_SC02_021/ov_SC02_021_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_021/ov_SC02_021_jr_8015C32C.c b/src/ov_SC02_021/ov_SC02_021_jr_8015C32C.c index 90c9ec119a..1555a40edd 100644 --- a/src/ov_SC02_021/ov_SC02_021_jr_8015C32C.c +++ b/src/ov_SC02_021/ov_SC02_021_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80182580[]; + extern unsigned short D_80182582[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80182580[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80182582[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801825B0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801825B0)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_021/ov_SC02_021_jr_8016AB6C.c b/src/ov_SC02_021/ov_SC02_021_jr_8016AB6C.c index d81a2fc411..b8b5ec4d16 100644 --- a/src/ov_SC02_021/ov_SC02_021_jr_8016AB6C.c +++ b/src/ov_SC02_021/ov_SC02_021_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_021/nonmatchings/ov_SC02_021_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80182FF8[])(void); diff --git a/src/ov_SC02_026/ov_SC02_026_after.c b/src/ov_SC02_026/ov_SC02_026_after.c index 18df669324..8f83e51fe7 100644 --- a/src/ov_SC02_026/ov_SC02_026_after.c +++ b/src/ov_SC02_026/ov_SC02_026_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_026/ov_SC02_026_jr_8012ACE0.c b/src/ov_SC02_026/ov_SC02_026_jr_8012ACE0.c index 39ac11df78..a916dedb61 100644 --- a/src/ov_SC02_026/ov_SC02_026_jr_8012ACE0.c +++ b/src/ov_SC02_026/ov_SC02_026_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_026/ov_SC02_026_jr_80135D20.c b/src/ov_SC02_026/ov_SC02_026_jr_80135D20.c index 73bb8b7588..854bc490bd 100644 --- a/src/ov_SC02_026/ov_SC02_026_jr_80135D20.c +++ b/src/ov_SC02_026/ov_SC02_026_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_026/ov_SC02_026_jr_80140608.c b/src/ov_SC02_026/ov_SC02_026_jr_80140608.c index aec3d4874c..1764f7b984 100644 --- a/src/ov_SC02_026/ov_SC02_026_jr_80140608.c +++ b/src/ov_SC02_026/ov_SC02_026_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80187818[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80187818[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_026/ov_SC02_026_jr_80154C24.c b/src/ov_SC02_026/ov_SC02_026_jr_80154C24.c index a6dcd59208..c9f6fa5e0e 100644 --- a/src/ov_SC02_026/ov_SC02_026_jr_80154C24.c +++ b/src/ov_SC02_026/ov_SC02_026_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_026/ov_SC02_026_jr_8015C32C.c b/src/ov_SC02_026/ov_SC02_026_jr_8015C32C.c index 6a2237d5bd..68108677c9 100644 --- a/src/ov_SC02_026/ov_SC02_026_jr_8015C32C.c +++ b/src/ov_SC02_026/ov_SC02_026_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80188BD4[]; + extern unsigned short D_80188BD6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80188BD4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80188BD6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80188C04; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80188C04)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_026/ov_SC02_026_jr_8016AB6C.c b/src/ov_SC02_026/ov_SC02_026_jr_8016AB6C.c index 25446d0426..0381c58fea 100644 --- a/src/ov_SC02_026/ov_SC02_026_jr_8016AB6C.c +++ b/src/ov_SC02_026/ov_SC02_026_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_026/nonmatchings/ov_SC02_026_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018964C[])(void); diff --git a/src/ov_SC02_027/ov_SC02_027_after.c b/src/ov_SC02_027/ov_SC02_027_after.c index 1464108d1b..e42b58911e 100644 --- a/src/ov_SC02_027/ov_SC02_027_after.c +++ b/src/ov_SC02_027/ov_SC02_027_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_027/ov_SC02_027_jr_8012ACE0.c b/src/ov_SC02_027/ov_SC02_027_jr_8012ACE0.c index cc5d042c5d..7a1546b03b 100644 --- a/src/ov_SC02_027/ov_SC02_027_jr_8012ACE0.c +++ b/src/ov_SC02_027/ov_SC02_027_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_027/ov_SC02_027_jr_80135D20.c b/src/ov_SC02_027/ov_SC02_027_jr_80135D20.c index 776faa822b..21f367188b 100644 --- a/src/ov_SC02_027/ov_SC02_027_jr_80135D20.c +++ b/src/ov_SC02_027/ov_SC02_027_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_027/ov_SC02_027_jr_80140608.c b/src/ov_SC02_027/ov_SC02_027_jr_80140608.c index 11317afbba..7fa95384c7 100644 --- a/src/ov_SC02_027/ov_SC02_027_jr_80140608.c +++ b/src/ov_SC02_027/ov_SC02_027_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018C93C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018C93C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_027/ov_SC02_027_jr_80154C24.c b/src/ov_SC02_027/ov_SC02_027_jr_80154C24.c index d8bc121b4f..bfdd309711 100644 --- a/src/ov_SC02_027/ov_SC02_027_jr_80154C24.c +++ b/src/ov_SC02_027/ov_SC02_027_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_027/ov_SC02_027_jr_8015C32C.c b/src/ov_SC02_027/ov_SC02_027_jr_8015C32C.c index 337f756755..ddcd83a574 100644 --- a/src/ov_SC02_027/ov_SC02_027_jr_8015C32C.c +++ b/src/ov_SC02_027/ov_SC02_027_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018DCF8[]; + extern unsigned short D_8018DCFA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018DCF8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018DCFA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018DD28; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018DD28)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_027/ov_SC02_027_jr_8016AB6C.c b/src/ov_SC02_027/ov_SC02_027_jr_8016AB6C.c index 9a705ddc07..bde46484bd 100644 --- a/src/ov_SC02_027/ov_SC02_027_jr_8016AB6C.c +++ b/src/ov_SC02_027/ov_SC02_027_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_027/nonmatchings/ov_SC02_027_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018E770[])(void); diff --git a/src/ov_SC02_028/ov_SC02_028_after.c b/src/ov_SC02_028/ov_SC02_028_after.c index f20c4f92b2..800d1d27ba 100644 --- a/src/ov_SC02_028/ov_SC02_028_after.c +++ b/src/ov_SC02_028/ov_SC02_028_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_028/ov_SC02_028_jr_8012ACE0.c b/src/ov_SC02_028/ov_SC02_028_jr_8012ACE0.c index f89eccd2f7..67cc38f239 100644 --- a/src/ov_SC02_028/ov_SC02_028_jr_8012ACE0.c +++ b/src/ov_SC02_028/ov_SC02_028_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_028/ov_SC02_028_jr_80135D20.c b/src/ov_SC02_028/ov_SC02_028_jr_80135D20.c index 1d47c227d1..0bc3560cd2 100644 --- a/src/ov_SC02_028/ov_SC02_028_jr_80135D20.c +++ b/src/ov_SC02_028/ov_SC02_028_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_028/ov_SC02_028_jr_80140608.c b/src/ov_SC02_028/ov_SC02_028_jr_80140608.c index c484d3d2e0..b57a4b15df 100644 --- a/src/ov_SC02_028/ov_SC02_028_jr_80140608.c +++ b/src/ov_SC02_028/ov_SC02_028_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018CB40[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018CB40[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_028/ov_SC02_028_jr_80154C24.c b/src/ov_SC02_028/ov_SC02_028_jr_80154C24.c index 1d87563f76..2c84ecda42 100644 --- a/src/ov_SC02_028/ov_SC02_028_jr_80154C24.c +++ b/src/ov_SC02_028/ov_SC02_028_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_028/ov_SC02_028_jr_8015C32C.c b/src/ov_SC02_028/ov_SC02_028_jr_8015C32C.c index 1dd439ae09..dc9e5e7357 100644 --- a/src/ov_SC02_028/ov_SC02_028_jr_8015C32C.c +++ b/src/ov_SC02_028/ov_SC02_028_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018DEFC[]; + extern unsigned short D_8018DEFE[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018DEFC[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018DEFE[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018DF2C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018DF2C)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_028/ov_SC02_028_jr_8016AB6C.c b/src/ov_SC02_028/ov_SC02_028_jr_8016AB6C.c index 1b779776ce..43ccdf1a15 100644 --- a/src/ov_SC02_028/ov_SC02_028_jr_8016AB6C.c +++ b/src/ov_SC02_028/ov_SC02_028_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_028/nonmatchings/ov_SC02_028_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018E974[])(void); diff --git a/src/ov_SC02_031/ov_SC02_031_after.c b/src/ov_SC02_031/ov_SC02_031_after.c index fba4951bda..d30b64026e 100644 --- a/src/ov_SC02_031/ov_SC02_031_after.c +++ b/src/ov_SC02_031/ov_SC02_031_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_031/ov_SC02_031_jr_8012ACE0.c b/src/ov_SC02_031/ov_SC02_031_jr_8012ACE0.c index 21bc1d9803..c3d6e532c2 100644 --- a/src/ov_SC02_031/ov_SC02_031_jr_8012ACE0.c +++ b/src/ov_SC02_031/ov_SC02_031_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_031/ov_SC02_031_jr_80135D20.c b/src/ov_SC02_031/ov_SC02_031_jr_80135D20.c index b2a365d058..a62211de7f 100644 --- a/src/ov_SC02_031/ov_SC02_031_jr_80135D20.c +++ b/src/ov_SC02_031/ov_SC02_031_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_031/ov_SC02_031_jr_80140608.c b/src/ov_SC02_031/ov_SC02_031_jr_80140608.c index d7e58bfd07..8983f075f6 100644 --- a/src/ov_SC02_031/ov_SC02_031_jr_80140608.c +++ b/src/ov_SC02_031/ov_SC02_031_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018636C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018636C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_031/ov_SC02_031_jr_80154C24.c b/src/ov_SC02_031/ov_SC02_031_jr_80154C24.c index 9bb0eab49d..399723c894 100644 --- a/src/ov_SC02_031/ov_SC02_031_jr_80154C24.c +++ b/src/ov_SC02_031/ov_SC02_031_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_031/ov_SC02_031_jr_8015C32C.c b/src/ov_SC02_031/ov_SC02_031_jr_8015C32C.c index 143c511c15..7e1b463f0b 100644 --- a/src/ov_SC02_031/ov_SC02_031_jr_8015C32C.c +++ b/src/ov_SC02_031/ov_SC02_031_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80187728[]; + extern unsigned short D_8018772A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80187728[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018772A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80187758; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80187758)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_031/ov_SC02_031_jr_8016AB6C.c b/src/ov_SC02_031/ov_SC02_031_jr_8016AB6C.c index 269633871b..f1d3b9aba8 100644 --- a/src/ov_SC02_031/ov_SC02_031_jr_8016AB6C.c +++ b/src/ov_SC02_031/ov_SC02_031_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_031/nonmatchings/ov_SC02_031_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801881A0[])(void); diff --git a/src/ov_SC02_035/ov_SC02_035_after.c b/src/ov_SC02_035/ov_SC02_035_after.c index 5034769914..948842ff33 100644 --- a/src/ov_SC02_035/ov_SC02_035_after.c +++ b/src/ov_SC02_035/ov_SC02_035_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_035/ov_SC02_035_jr_8012ACE0.c b/src/ov_SC02_035/ov_SC02_035_jr_8012ACE0.c index 2d7142ce9f..dfd543c8fe 100644 --- a/src/ov_SC02_035/ov_SC02_035_jr_8012ACE0.c +++ b/src/ov_SC02_035/ov_SC02_035_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_035/ov_SC02_035_jr_80135D20.c b/src/ov_SC02_035/ov_SC02_035_jr_80135D20.c index 9e6c4bfa78..c2a6c84ef5 100644 --- a/src/ov_SC02_035/ov_SC02_035_jr_80135D20.c +++ b/src/ov_SC02_035/ov_SC02_035_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_035/ov_SC02_035_jr_80140608.c b/src/ov_SC02_035/ov_SC02_035_jr_80140608.c index 2685761dab..7c0dd7f878 100644 --- a/src/ov_SC02_035/ov_SC02_035_jr_80140608.c +++ b/src/ov_SC02_035/ov_SC02_035_jr_80140608.c @@ -1812,7 +1812,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80185BCC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80185BCC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_035/ov_SC02_035_jr_80154C24.c b/src/ov_SC02_035/ov_SC02_035_jr_80154C24.c index 9ab8119080..d7bf81f624 100644 --- a/src/ov_SC02_035/ov_SC02_035_jr_80154C24.c +++ b/src/ov_SC02_035/ov_SC02_035_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_035/ov_SC02_035_jr_8015C32C.c b/src/ov_SC02_035/ov_SC02_035_jr_8015C32C.c index 099fd6089c..3ba2c2dd8d 100644 --- a/src/ov_SC02_035/ov_SC02_035_jr_8015C32C.c +++ b/src/ov_SC02_035/ov_SC02_035_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80186F88[]; + extern unsigned short D_80186F8A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80186F88[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80186F8A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80186FB8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80186FB8)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_035/ov_SC02_035_jr_8016AB6C.c b/src/ov_SC02_035/ov_SC02_035_jr_8016AB6C.c index 9387170680..52fd969d4f 100644 --- a/src/ov_SC02_035/ov_SC02_035_jr_8016AB6C.c +++ b/src/ov_SC02_035/ov_SC02_035_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_035/nonmatchings/ov_SC02_035_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80187A00[])(void); diff --git a/src/ov_SC02_039/ov_SC02_039_after.c b/src/ov_SC02_039/ov_SC02_039_after.c index 16142b0038..46c7be132a 100644 --- a/src/ov_SC02_039/ov_SC02_039_after.c +++ b/src/ov_SC02_039/ov_SC02_039_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_039/ov_SC02_039_jr_8012ACE0.c b/src/ov_SC02_039/ov_SC02_039_jr_8012ACE0.c index c0b6b8792a..b0bda13401 100644 --- a/src/ov_SC02_039/ov_SC02_039_jr_8012ACE0.c +++ b/src/ov_SC02_039/ov_SC02_039_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_039/ov_SC02_039_jr_80135D20.c b/src/ov_SC02_039/ov_SC02_039_jr_80135D20.c index 2dd3190767..7a1f4b9648 100644 --- a/src/ov_SC02_039/ov_SC02_039_jr_80135D20.c +++ b/src/ov_SC02_039/ov_SC02_039_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_039/ov_SC02_039_jr_80140608.c b/src/ov_SC02_039/ov_SC02_039_jr_80140608.c index cf91c9a7a7..670e15c372 100644 --- a/src/ov_SC02_039/ov_SC02_039_jr_80140608.c +++ b/src/ov_SC02_039/ov_SC02_039_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801807F8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801807F8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_039/ov_SC02_039_jr_80154C24.c b/src/ov_SC02_039/ov_SC02_039_jr_80154C24.c index 8482cfcf33..dfc0c901b0 100644 --- a/src/ov_SC02_039/ov_SC02_039_jr_80154C24.c +++ b/src/ov_SC02_039/ov_SC02_039_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_039/ov_SC02_039_jr_8015C32C.c b/src/ov_SC02_039/ov_SC02_039_jr_8015C32C.c index 0ca77cef61..3b49b61133 100644 --- a/src/ov_SC02_039/ov_SC02_039_jr_8015C32C.c +++ b/src/ov_SC02_039/ov_SC02_039_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80181BB4[]; + extern unsigned short D_80181BB6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80181BB4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80181BB6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80181BE4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80181BE4)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_039/ov_SC02_039_jr_8016AB6C.c b/src/ov_SC02_039/ov_SC02_039_jr_8016AB6C.c index a579955681..c47e11c74a 100644 --- a/src/ov_SC02_039/ov_SC02_039_jr_8016AB6C.c +++ b/src/ov_SC02_039/ov_SC02_039_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_039/nonmatchings/ov_SC02_039_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018262C[])(void); diff --git a/src/ov_SC02_041/ov_SC02_041_after.c b/src/ov_SC02_041/ov_SC02_041_after.c index 95f1672ac4..1f07b752f2 100644 --- a/src/ov_SC02_041/ov_SC02_041_after.c +++ b/src/ov_SC02_041/ov_SC02_041_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC02_041/ov_SC02_041_jr_8012ACE0.c b/src/ov_SC02_041/ov_SC02_041_jr_8012ACE0.c index 0cab081498..bc4649df58 100644 --- a/src/ov_SC02_041/ov_SC02_041_jr_8012ACE0.c +++ b/src/ov_SC02_041/ov_SC02_041_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC02_041/ov_SC02_041_jr_80135D20.c b/src/ov_SC02_041/ov_SC02_041_jr_80135D20.c index ee3178f8d5..bfb68b25c5 100644 --- a/src/ov_SC02_041/ov_SC02_041_jr_80135D20.c +++ b/src/ov_SC02_041/ov_SC02_041_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC02_041/ov_SC02_041_jr_80140608.c b/src/ov_SC02_041/ov_SC02_041_jr_80140608.c index d5244f91bf..b832ea9737 100644 --- a/src/ov_SC02_041/ov_SC02_041_jr_80140608.c +++ b/src/ov_SC02_041/ov_SC02_041_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801864C8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801864C8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC02_041/ov_SC02_041_jr_80154C24.c b/src/ov_SC02_041/ov_SC02_041_jr_80154C24.c index 2c389bf7d6..aba07ccece 100644 --- a/src/ov_SC02_041/ov_SC02_041_jr_80154C24.c +++ b/src/ov_SC02_041/ov_SC02_041_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC02_041/ov_SC02_041_jr_8015C32C.c b/src/ov_SC02_041/ov_SC02_041_jr_8015C32C.c index 2ec8df8761..d70f3c5be8 100644 --- a/src/ov_SC02_041/ov_SC02_041_jr_8015C32C.c +++ b/src/ov_SC02_041/ov_SC02_041_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80187884[]; + extern unsigned short D_80187886[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80187884[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80187886[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801878B4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801878B4)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC02_041/ov_SC02_041_jr_8016AB6C.c b/src/ov_SC02_041/ov_SC02_041_jr_8016AB6C.c index 6dfdf9cd0a..a14cb814f3 100644 --- a/src/ov_SC02_041/ov_SC02_041_jr_8016AB6C.c +++ b/src/ov_SC02_041/ov_SC02_041_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC02_041/nonmatchings/ov_SC02_041_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801882FC[])(void); diff --git a/src/ov_SC03_001/ov_SC03_001_after.c b/src/ov_SC03_001/ov_SC03_001_after.c index a720dc3411..88ce05e5d6 100644 --- a/src/ov_SC03_001/ov_SC03_001_after.c +++ b/src/ov_SC03_001/ov_SC03_001_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_001/ov_SC03_001_jr_8012ACE0.c b/src/ov_SC03_001/ov_SC03_001_jr_8012ACE0.c index d38d65c60c..6ee9fd971e 100644 --- a/src/ov_SC03_001/ov_SC03_001_jr_8012ACE0.c +++ b/src/ov_SC03_001/ov_SC03_001_jr_8012ACE0.c @@ -986,7 +986,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1896,9 +2031,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_001/ov_SC03_001_jr_80135D20.c b/src/ov_SC03_001/ov_SC03_001_jr_80135D20.c index 88882d4ab9..995be554a2 100644 --- a/src/ov_SC03_001/ov_SC03_001_jr_80135D20.c +++ b/src/ov_SC03_001/ov_SC03_001_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_001/ov_SC03_001_jr_80140608.c b/src/ov_SC03_001/ov_SC03_001_jr_80140608.c index 60730df140..9e9d453306 100644 --- a/src/ov_SC03_001/ov_SC03_001_jr_80140608.c +++ b/src/ov_SC03_001/ov_SC03_001_jr_80140608.c @@ -1827,7 +1827,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018EC08[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018EC08[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_001/ov_SC03_001_jr_80154C24.c b/src/ov_SC03_001/ov_SC03_001_jr_80154C24.c index dff4b12870..870a323b80 100644 --- a/src/ov_SC03_001/ov_SC03_001_jr_80154C24.c +++ b/src/ov_SC03_001/ov_SC03_001_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_001/ov_SC03_001_jr_8015C32C.c b/src/ov_SC03_001/ov_SC03_001_jr_8015C32C.c index 0f412ee0a4..d4d3161563 100644 --- a/src/ov_SC03_001/ov_SC03_001_jr_8015C32C.c +++ b/src/ov_SC03_001/ov_SC03_001_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018FFC4[]; + extern unsigned short D_8018FFC6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018FFC4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018FFC6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018FFF4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018FFF4)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_001/ov_SC03_001_jr_8016AB6C.c b/src/ov_SC03_001/ov_SC03_001_jr_8016AB6C.c index dfe9982852..29cf3a0926 100644 --- a/src/ov_SC03_001/ov_SC03_001_jr_8016AB6C.c +++ b/src/ov_SC03_001/ov_SC03_001_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_001/nonmatchings/ov_SC03_001_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80190A3C[])(void); diff --git a/src/ov_SC03_002/ov_SC03_002_after.c b/src/ov_SC03_002/ov_SC03_002_after.c index 06c7b363b9..0915c93b6f 100644 --- a/src/ov_SC03_002/ov_SC03_002_after.c +++ b/src/ov_SC03_002/ov_SC03_002_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_002/ov_SC03_002_jr_8012ACE0.c b/src/ov_SC03_002/ov_SC03_002_jr_8012ACE0.c index ddf56bbe17..cd16cc541e 100644 --- a/src/ov_SC03_002/ov_SC03_002_jr_8012ACE0.c +++ b/src/ov_SC03_002/ov_SC03_002_jr_8012ACE0.c @@ -990,7 +990,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1900,9 +2035,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_002/ov_SC03_002_jr_80135D20.c b/src/ov_SC03_002/ov_SC03_002_jr_80135D20.c index 2ea58efe77..e4c091f8a4 100644 --- a/src/ov_SC03_002/ov_SC03_002_jr_80135D20.c +++ b/src/ov_SC03_002/ov_SC03_002_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_002/ov_SC03_002_jr_80140608.c b/src/ov_SC03_002/ov_SC03_002_jr_80140608.c index b1a56f6b12..f85dcfaedf 100644 --- a/src/ov_SC03_002/ov_SC03_002_jr_80140608.c +++ b/src/ov_SC03_002/ov_SC03_002_jr_80140608.c @@ -1811,7 +1811,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80186BF8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80186BF8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_002/ov_SC03_002_jr_80154C24.c b/src/ov_SC03_002/ov_SC03_002_jr_80154C24.c index cdf23134a8..dd0bc5f4ab 100644 --- a/src/ov_SC03_002/ov_SC03_002_jr_80154C24.c +++ b/src/ov_SC03_002/ov_SC03_002_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_002/ov_SC03_002_jr_8015C32C.c b/src/ov_SC03_002/ov_SC03_002_jr_8015C32C.c index 876c0f62e6..be7788655c 100644 --- a/src/ov_SC03_002/ov_SC03_002_jr_8015C32C.c +++ b/src/ov_SC03_002/ov_SC03_002_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80187FB4[]; + extern unsigned short D_80187FB6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80187FB4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80187FB6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80187FE4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80187FE4)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_002/ov_SC03_002_jr_8016AB6C.c b/src/ov_SC03_002/ov_SC03_002_jr_8016AB6C.c index 3fdd1f95e6..5214b130c1 100644 --- a/src/ov_SC03_002/ov_SC03_002_jr_8016AB6C.c +++ b/src/ov_SC03_002/ov_SC03_002_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_002/nonmatchings/ov_SC03_002_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80188A2C[])(void); diff --git a/src/ov_SC03_003/ov_SC03_003_after.c b/src/ov_SC03_003/ov_SC03_003_after.c index 082c3fb8ee..90e2f66d8f 100644 --- a/src/ov_SC03_003/ov_SC03_003_after.c +++ b/src/ov_SC03_003/ov_SC03_003_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_003/ov_SC03_003_jr_8012ACE0.c b/src/ov_SC03_003/ov_SC03_003_jr_8012ACE0.c index e7d74be6b0..78673d0044 100644 --- a/src/ov_SC03_003/ov_SC03_003_jr_8012ACE0.c +++ b/src/ov_SC03_003/ov_SC03_003_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_003/ov_SC03_003_jr_80135D20.c b/src/ov_SC03_003/ov_SC03_003_jr_80135D20.c index a07bacff92..c28c14b17d 100644 --- a/src/ov_SC03_003/ov_SC03_003_jr_80135D20.c +++ b/src/ov_SC03_003/ov_SC03_003_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_003/ov_SC03_003_jr_80140608.c b/src/ov_SC03_003/ov_SC03_003_jr_80140608.c index 430ac7088f..1b2e732b84 100644 --- a/src/ov_SC03_003/ov_SC03_003_jr_80140608.c +++ b/src/ov_SC03_003/ov_SC03_003_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801809C0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801809C0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_003/ov_SC03_003_jr_80154C24.c b/src/ov_SC03_003/ov_SC03_003_jr_80154C24.c index 34c070917e..62c8d0ff92 100644 --- a/src/ov_SC03_003/ov_SC03_003_jr_80154C24.c +++ b/src/ov_SC03_003/ov_SC03_003_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_003/ov_SC03_003_jr_8015C32C.c b/src/ov_SC03_003/ov_SC03_003_jr_8015C32C.c index 5da6cdf18c..657c99027a 100644 --- a/src/ov_SC03_003/ov_SC03_003_jr_8015C32C.c +++ b/src/ov_SC03_003/ov_SC03_003_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80181D7C[]; + extern unsigned short D_80181D7E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80181D7C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80181D7E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80181DAC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80181DAC)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_003/ov_SC03_003_jr_8016AB6C.c b/src/ov_SC03_003/ov_SC03_003_jr_8016AB6C.c index 0c521f75ed..5362cec861 100644 --- a/src/ov_SC03_003/ov_SC03_003_jr_8016AB6C.c +++ b/src/ov_SC03_003/ov_SC03_003_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_003/nonmatchings/ov_SC03_003_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801827F4[])(void); diff --git a/src/ov_SC03_006/ov_SC03_006_after.c b/src/ov_SC03_006/ov_SC03_006_after.c index e1be635cbf..7724c56872 100644 --- a/src/ov_SC03_006/ov_SC03_006_after.c +++ b/src/ov_SC03_006/ov_SC03_006_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_006/ov_SC03_006_jr_8012ACE0.c b/src/ov_SC03_006/ov_SC03_006_jr_8012ACE0.c index 64d7285619..226e8f58f2 100644 --- a/src/ov_SC03_006/ov_SC03_006_jr_8012ACE0.c +++ b/src/ov_SC03_006/ov_SC03_006_jr_8012ACE0.c @@ -990,7 +990,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1900,9 +2035,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_006/ov_SC03_006_jr_80135D20.c b/src/ov_SC03_006/ov_SC03_006_jr_80135D20.c index b1ae67ceb8..2a1f5857f9 100644 --- a/src/ov_SC03_006/ov_SC03_006_jr_80135D20.c +++ b/src/ov_SC03_006/ov_SC03_006_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_006/ov_SC03_006_jr_80140608.c b/src/ov_SC03_006/ov_SC03_006_jr_80140608.c index d87334e18f..0acd209621 100644 --- a/src/ov_SC03_006/ov_SC03_006_jr_80140608.c +++ b/src/ov_SC03_006/ov_SC03_006_jr_80140608.c @@ -1827,7 +1827,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018DCDC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018DCDC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_006/ov_SC03_006_jr_80154C24.c b/src/ov_SC03_006/ov_SC03_006_jr_80154C24.c index f9bb5ba57a..ac93efae53 100644 --- a/src/ov_SC03_006/ov_SC03_006_jr_80154C24.c +++ b/src/ov_SC03_006/ov_SC03_006_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_006/ov_SC03_006_jr_8015C32C.c b/src/ov_SC03_006/ov_SC03_006_jr_8015C32C.c index fab3f2a3ac..cc16a9bab8 100644 --- a/src/ov_SC03_006/ov_SC03_006_jr_8015C32C.c +++ b/src/ov_SC03_006/ov_SC03_006_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018F098[]; + extern unsigned short D_8018F09A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018F098[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018F09A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018F0C8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018F0C8)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_006/ov_SC03_006_jr_8016AB6C.c b/src/ov_SC03_006/ov_SC03_006_jr_8016AB6C.c index 332a49c1a5..502d15f183 100644 --- a/src/ov_SC03_006/ov_SC03_006_jr_8016AB6C.c +++ b/src/ov_SC03_006/ov_SC03_006_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_006/nonmatchings/ov_SC03_006_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018FB10[])(void); diff --git a/src/ov_SC03_007/ov_SC03_007_after.c b/src/ov_SC03_007/ov_SC03_007_after.c index 1da0d1c47b..f2e33a34df 100644 --- a/src/ov_SC03_007/ov_SC03_007_after.c +++ b/src/ov_SC03_007/ov_SC03_007_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_007/ov_SC03_007_jr_8012ACE0.c b/src/ov_SC03_007/ov_SC03_007_jr_8012ACE0.c index 71ef2f0023..063deb25ea 100644 --- a/src/ov_SC03_007/ov_SC03_007_jr_8012ACE0.c +++ b/src/ov_SC03_007/ov_SC03_007_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_007/ov_SC03_007_jr_80135D20.c b/src/ov_SC03_007/ov_SC03_007_jr_80135D20.c index 82a55dc41f..31e74e633d 100644 --- a/src/ov_SC03_007/ov_SC03_007_jr_80135D20.c +++ b/src/ov_SC03_007/ov_SC03_007_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_007/ov_SC03_007_jr_80140608.c b/src/ov_SC03_007/ov_SC03_007_jr_80140608.c index 6b53ff6854..1800218e5f 100644 --- a/src/ov_SC03_007/ov_SC03_007_jr_80140608.c +++ b/src/ov_SC03_007/ov_SC03_007_jr_80140608.c @@ -1825,7 +1825,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80188AD4[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80188AD4[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_007/ov_SC03_007_jr_80154C24.c b/src/ov_SC03_007/ov_SC03_007_jr_80154C24.c index ac708932aa..a31d411ed5 100644 --- a/src/ov_SC03_007/ov_SC03_007_jr_80154C24.c +++ b/src/ov_SC03_007/ov_SC03_007_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_007/ov_SC03_007_jr_8015C32C.c b/src/ov_SC03_007/ov_SC03_007_jr_8015C32C.c index 27a45b1204..8d26d189cd 100644 --- a/src/ov_SC03_007/ov_SC03_007_jr_8015C32C.c +++ b/src/ov_SC03_007/ov_SC03_007_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80189E90[]; + extern unsigned short D_80189E92[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80189E90[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80189E92[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80189EC0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80189EC0)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_007/ov_SC03_007_jr_8016AB6C.c b/src/ov_SC03_007/ov_SC03_007_jr_8016AB6C.c index 1bfe65bec7..990bc01fdc 100644 --- a/src/ov_SC03_007/ov_SC03_007_jr_8016AB6C.c +++ b/src/ov_SC03_007/ov_SC03_007_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_007/nonmatchings/ov_SC03_007_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018A908[])(void); diff --git a/src/ov_SC03_010/ov_SC03_010_after.c b/src/ov_SC03_010/ov_SC03_010_after.c index e1fb2ad0ff..78903abf9c 100644 --- a/src/ov_SC03_010/ov_SC03_010_after.c +++ b/src/ov_SC03_010/ov_SC03_010_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_010/ov_SC03_010_jr_8012ACE0.c b/src/ov_SC03_010/ov_SC03_010_jr_8012ACE0.c index af42faafe1..f2f6ab5923 100644 --- a/src/ov_SC03_010/ov_SC03_010_jr_8012ACE0.c +++ b/src/ov_SC03_010/ov_SC03_010_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_010/ov_SC03_010_jr_80135D20.c b/src/ov_SC03_010/ov_SC03_010_jr_80135D20.c index f01a40421d..01ef83f182 100644 --- a/src/ov_SC03_010/ov_SC03_010_jr_80135D20.c +++ b/src/ov_SC03_010/ov_SC03_010_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_010/ov_SC03_010_jr_80140608.c b/src/ov_SC03_010/ov_SC03_010_jr_80140608.c index 82690ef0be..ec18513016 100644 --- a/src/ov_SC03_010/ov_SC03_010_jr_80140608.c +++ b/src/ov_SC03_010/ov_SC03_010_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801815F4[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801815F4[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_010/ov_SC03_010_jr_80154C24.c b/src/ov_SC03_010/ov_SC03_010_jr_80154C24.c index bb0e038917..a20e8a4f0f 100644 --- a/src/ov_SC03_010/ov_SC03_010_jr_80154C24.c +++ b/src/ov_SC03_010/ov_SC03_010_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_010/ov_SC03_010_jr_8015C32C.c b/src/ov_SC03_010/ov_SC03_010_jr_8015C32C.c index 26bff4d79e..613ef4910e 100644 --- a/src/ov_SC03_010/ov_SC03_010_jr_8015C32C.c +++ b/src/ov_SC03_010/ov_SC03_010_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801829B0[]; + extern unsigned short D_801829B2[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801829B0[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801829B2[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801829E0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801829E0)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_010/ov_SC03_010_jr_8016AB6C.c b/src/ov_SC03_010/ov_SC03_010_jr_8016AB6C.c index 32f580219b..6906863644 100644 --- a/src/ov_SC03_010/ov_SC03_010_jr_8016AB6C.c +++ b/src/ov_SC03_010/ov_SC03_010_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80183428[])(void); diff --git a/src/ov_SC03_011/ov_SC03_011_after.c b/src/ov_SC03_011/ov_SC03_011_after.c index c6f01d0d47..cc62f9778e 100644 --- a/src/ov_SC03_011/ov_SC03_011_after.c +++ b/src/ov_SC03_011/ov_SC03_011_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_011/ov_SC03_011_jr_8012ACE0.c b/src/ov_SC03_011/ov_SC03_011_jr_8012ACE0.c index c63fe424a8..803545c931 100644 --- a/src/ov_SC03_011/ov_SC03_011_jr_8012ACE0.c +++ b/src/ov_SC03_011/ov_SC03_011_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_011/ov_SC03_011_jr_80135D20.c b/src/ov_SC03_011/ov_SC03_011_jr_80135D20.c index a35c078fa0..ab7568b016 100644 --- a/src/ov_SC03_011/ov_SC03_011_jr_80135D20.c +++ b/src/ov_SC03_011/ov_SC03_011_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_011/ov_SC03_011_jr_80140608.c b/src/ov_SC03_011/ov_SC03_011_jr_80140608.c index c91307648a..30cb715c3d 100644 --- a/src/ov_SC03_011/ov_SC03_011_jr_80140608.c +++ b/src/ov_SC03_011/ov_SC03_011_jr_80140608.c @@ -1843,7 +1843,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80182964[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80182964[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_011/ov_SC03_011_jr_80154C24.c b/src/ov_SC03_011/ov_SC03_011_jr_80154C24.c index d514d80aac..f011a1110a 100644 --- a/src/ov_SC03_011/ov_SC03_011_jr_80154C24.c +++ b/src/ov_SC03_011/ov_SC03_011_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_011/ov_SC03_011_jr_8015C32C.c b/src/ov_SC03_011/ov_SC03_011_jr_8015C32C.c index aed5bd66d9..3185e2e5ef 100644 --- a/src/ov_SC03_011/ov_SC03_011_jr_8015C32C.c +++ b/src/ov_SC03_011/ov_SC03_011_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80183D20[]; + extern unsigned short D_80183D22[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80183D20[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80183D22[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80183D50; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80183D50)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_011/ov_SC03_011_jr_8016AB6C.c b/src/ov_SC03_011/ov_SC03_011_jr_8016AB6C.c index 8a86d04b5f..180ae1780a 100644 --- a/src/ov_SC03_011/ov_SC03_011_jr_8016AB6C.c +++ b/src/ov_SC03_011/ov_SC03_011_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_011/nonmatchings/ov_SC03_011_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80184798[])(void); diff --git a/src/ov_SC03_012/ov_SC03_012_after.c b/src/ov_SC03_012/ov_SC03_012_after.c index 24630780ce..fa44ab87fd 100644 --- a/src/ov_SC03_012/ov_SC03_012_after.c +++ b/src/ov_SC03_012/ov_SC03_012_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_012/ov_SC03_012_jr_8012ACE0.c b/src/ov_SC03_012/ov_SC03_012_jr_8012ACE0.c index 39e33b6305..09a6170137 100644 --- a/src/ov_SC03_012/ov_SC03_012_jr_8012ACE0.c +++ b/src/ov_SC03_012/ov_SC03_012_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_012/ov_SC03_012_jr_80135D20.c b/src/ov_SC03_012/ov_SC03_012_jr_80135D20.c index 55745af26e..a3cad2862d 100644 --- a/src/ov_SC03_012/ov_SC03_012_jr_80135D20.c +++ b/src/ov_SC03_012/ov_SC03_012_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_012/ov_SC03_012_jr_80140608.c b/src/ov_SC03_012/ov_SC03_012_jr_80140608.c index c49b689030..c0d40e3bb9 100644 --- a/src/ov_SC03_012/ov_SC03_012_jr_80140608.c +++ b/src/ov_SC03_012/ov_SC03_012_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017F844[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017F844[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_012/ov_SC03_012_jr_80154C24.c b/src/ov_SC03_012/ov_SC03_012_jr_80154C24.c index d45faab1c3..9db2204e52 100644 --- a/src/ov_SC03_012/ov_SC03_012_jr_80154C24.c +++ b/src/ov_SC03_012/ov_SC03_012_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_012/ov_SC03_012_jr_8015C32C.c b/src/ov_SC03_012/ov_SC03_012_jr_8015C32C.c index 79992306bb..c25f976b5b 100644 --- a/src/ov_SC03_012/ov_SC03_012_jr_8015C32C.c +++ b/src/ov_SC03_012/ov_SC03_012_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80180C00[]; + extern unsigned short D_80180C02[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80180C00[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80180C02[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80180C30; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80180C30)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_012/ov_SC03_012_jr_8016AB6C.c b/src/ov_SC03_012/ov_SC03_012_jr_8016AB6C.c index 7c954cac21..4653f533b9 100644 --- a/src/ov_SC03_012/ov_SC03_012_jr_8016AB6C.c +++ b/src/ov_SC03_012/ov_SC03_012_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_012/nonmatchings/ov_SC03_012_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80181678[])(void); diff --git a/src/ov_SC03_013/ov_SC03_013_after.c b/src/ov_SC03_013/ov_SC03_013_after.c index ad6979fff6..0f6b6598b7 100644 --- a/src/ov_SC03_013/ov_SC03_013_after.c +++ b/src/ov_SC03_013/ov_SC03_013_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_013/ov_SC03_013_jr_8012ACE0.c b/src/ov_SC03_013/ov_SC03_013_jr_8012ACE0.c index c40b37e021..7bf79fac3e 100644 --- a/src/ov_SC03_013/ov_SC03_013_jr_8012ACE0.c +++ b/src/ov_SC03_013/ov_SC03_013_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_013/ov_SC03_013_jr_80135D20.c b/src/ov_SC03_013/ov_SC03_013_jr_80135D20.c index 5cfacc26e0..81def293b2 100644 --- a/src/ov_SC03_013/ov_SC03_013_jr_80135D20.c +++ b/src/ov_SC03_013/ov_SC03_013_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_013/ov_SC03_013_jr_80140608.c b/src/ov_SC03_013/ov_SC03_013_jr_80140608.c index 19087f33ee..930dc4b839 100644 --- a/src/ov_SC03_013/ov_SC03_013_jr_80140608.c +++ b/src/ov_SC03_013/ov_SC03_013_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018262C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018262C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_013/ov_SC03_013_jr_80154C24.c b/src/ov_SC03_013/ov_SC03_013_jr_80154C24.c index b1f2a5ee22..99e440c8a1 100644 --- a/src/ov_SC03_013/ov_SC03_013_jr_80154C24.c +++ b/src/ov_SC03_013/ov_SC03_013_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_013/ov_SC03_013_jr_8015C32C.c b/src/ov_SC03_013/ov_SC03_013_jr_8015C32C.c index 409864c7a2..938c867b06 100644 --- a/src/ov_SC03_013/ov_SC03_013_jr_8015C32C.c +++ b/src/ov_SC03_013/ov_SC03_013_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801839E8[]; + extern unsigned short D_801839EA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801839E8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801839EA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80183A18; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80183A18)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_013/ov_SC03_013_jr_8016AB6C.c b/src/ov_SC03_013/ov_SC03_013_jr_8016AB6C.c index 58818764a6..471d305f0f 100644 --- a/src/ov_SC03_013/ov_SC03_013_jr_8016AB6C.c +++ b/src/ov_SC03_013/ov_SC03_013_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_013/nonmatchings/ov_SC03_013_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80184460[])(void); diff --git a/src/ov_SC03_014/ov_SC03_014_after.c b/src/ov_SC03_014/ov_SC03_014_after.c index 7043f39881..8eb12a5717 100644 --- a/src/ov_SC03_014/ov_SC03_014_after.c +++ b/src/ov_SC03_014/ov_SC03_014_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_014/ov_SC03_014_jr_8012ACE0.c b/src/ov_SC03_014/ov_SC03_014_jr_8012ACE0.c index 30d4a494e6..bb60174bb1 100644 --- a/src/ov_SC03_014/ov_SC03_014_jr_8012ACE0.c +++ b/src/ov_SC03_014/ov_SC03_014_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_014/ov_SC03_014_jr_80135D20.c b/src/ov_SC03_014/ov_SC03_014_jr_80135D20.c index fe7277a258..e6b8df7235 100644 --- a/src/ov_SC03_014/ov_SC03_014_jr_80135D20.c +++ b/src/ov_SC03_014/ov_SC03_014_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_014/ov_SC03_014_jr_80140608.c b/src/ov_SC03_014/ov_SC03_014_jr_80140608.c index 9378826b12..9cc64e97d9 100644 --- a/src/ov_SC03_014/ov_SC03_014_jr_80140608.c +++ b/src/ov_SC03_014/ov_SC03_014_jr_80140608.c @@ -1810,7 +1810,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018CC68[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018CC68[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_014/ov_SC03_014_jr_80154C24.c b/src/ov_SC03_014/ov_SC03_014_jr_80154C24.c index ae7269b475..799fea0d28 100644 --- a/src/ov_SC03_014/ov_SC03_014_jr_80154C24.c +++ b/src/ov_SC03_014/ov_SC03_014_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_014/ov_SC03_014_jr_8015C32C.c b/src/ov_SC03_014/ov_SC03_014_jr_8015C32C.c index d656a6e0ab..ad78cc5fe8 100644 --- a/src/ov_SC03_014/ov_SC03_014_jr_8015C32C.c +++ b/src/ov_SC03_014/ov_SC03_014_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018E024[]; + extern unsigned short D_8018E026[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018E024[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018E026[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018E054; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018E054)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_014/ov_SC03_014_jr_8016AB6C.c b/src/ov_SC03_014/ov_SC03_014_jr_8016AB6C.c index dae5a25428..12b21deb3e 100644 --- a/src/ov_SC03_014/ov_SC03_014_jr_8016AB6C.c +++ b/src/ov_SC03_014/ov_SC03_014_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_014/nonmatchings/ov_SC03_014_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018EA9C[])(void); diff --git a/src/ov_SC03_015/ov_SC03_015_after.c b/src/ov_SC03_015/ov_SC03_015_after.c index b8c691ef85..348aae6f47 100644 --- a/src/ov_SC03_015/ov_SC03_015_after.c +++ b/src/ov_SC03_015/ov_SC03_015_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_015/ov_SC03_015_jr_8012ACE0.c b/src/ov_SC03_015/ov_SC03_015_jr_8012ACE0.c index 6952425f5c..8ee47ade55 100644 --- a/src/ov_SC03_015/ov_SC03_015_jr_8012ACE0.c +++ b/src/ov_SC03_015/ov_SC03_015_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_015/ov_SC03_015_jr_80135D20.c b/src/ov_SC03_015/ov_SC03_015_jr_80135D20.c index e4996b7d87..2b3a5242b3 100644 --- a/src/ov_SC03_015/ov_SC03_015_jr_80135D20.c +++ b/src/ov_SC03_015/ov_SC03_015_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_015/ov_SC03_015_jr_80140608.c b/src/ov_SC03_015/ov_SC03_015_jr_80140608.c index c0b481d00b..c12e9a92d5 100644 --- a/src/ov_SC03_015/ov_SC03_015_jr_80140608.c +++ b/src/ov_SC03_015/ov_SC03_015_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018CC68[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018CC68[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_015/ov_SC03_015_jr_80154C24.c b/src/ov_SC03_015/ov_SC03_015_jr_80154C24.c index cb45fd4a64..86960123db 100644 --- a/src/ov_SC03_015/ov_SC03_015_jr_80154C24.c +++ b/src/ov_SC03_015/ov_SC03_015_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_015/ov_SC03_015_jr_8015C32C.c b/src/ov_SC03_015/ov_SC03_015_jr_8015C32C.c index 32836b7b61..f09dad629f 100644 --- a/src/ov_SC03_015/ov_SC03_015_jr_8015C32C.c +++ b/src/ov_SC03_015/ov_SC03_015_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018E024[]; + extern unsigned short D_8018E026[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018E024[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018E026[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018E054; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018E054)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_015/ov_SC03_015_jr_8016AB6C.c b/src/ov_SC03_015/ov_SC03_015_jr_8016AB6C.c index 6b93f07ef7..7b08ff653b 100644 --- a/src/ov_SC03_015/ov_SC03_015_jr_8016AB6C.c +++ b/src/ov_SC03_015/ov_SC03_015_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_015/nonmatchings/ov_SC03_015_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018EA9C[])(void); diff --git a/src/ov_SC03_023/ov_SC03_023_after.c b/src/ov_SC03_023/ov_SC03_023_after.c index 4e3bbaa89a..ff40dbe6e0 100644 --- a/src/ov_SC03_023/ov_SC03_023_after.c +++ b/src/ov_SC03_023/ov_SC03_023_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_023/ov_SC03_023_jr_8012ACE0.c b/src/ov_SC03_023/ov_SC03_023_jr_8012ACE0.c index 14870a68e5..3458c6aa68 100644 --- a/src/ov_SC03_023/ov_SC03_023_jr_8012ACE0.c +++ b/src/ov_SC03_023/ov_SC03_023_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_023/ov_SC03_023_jr_80135D20.c b/src/ov_SC03_023/ov_SC03_023_jr_80135D20.c index 473fdc874f..bc2f49c84f 100644 --- a/src/ov_SC03_023/ov_SC03_023_jr_80135D20.c +++ b/src/ov_SC03_023/ov_SC03_023_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_023/ov_SC03_023_jr_80140608.c b/src/ov_SC03_023/ov_SC03_023_jr_80140608.c index 6c4a15f846..cff18d14b1 100644 --- a/src/ov_SC03_023/ov_SC03_023_jr_80140608.c +++ b/src/ov_SC03_023/ov_SC03_023_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801805E4[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801805E4[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_023/ov_SC03_023_jr_80154C24.c b/src/ov_SC03_023/ov_SC03_023_jr_80154C24.c index bd7655fe1b..980320e9b6 100644 --- a/src/ov_SC03_023/ov_SC03_023_jr_80154C24.c +++ b/src/ov_SC03_023/ov_SC03_023_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_023/ov_SC03_023_jr_8015C32C.c b/src/ov_SC03_023/ov_SC03_023_jr_8015C32C.c index ec13e961e3..e0771cde8e 100644 --- a/src/ov_SC03_023/ov_SC03_023_jr_8015C32C.c +++ b/src/ov_SC03_023/ov_SC03_023_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801819A0[]; + extern unsigned short D_801819A2[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801819A0[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801819A2[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801819D0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801819D0)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_023/ov_SC03_023_jr_8016AB6C.c b/src/ov_SC03_023/ov_SC03_023_jr_8016AB6C.c index b9e98aa595..bf3b9f661b 100644 --- a/src/ov_SC03_023/ov_SC03_023_jr_8016AB6C.c +++ b/src/ov_SC03_023/ov_SC03_023_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_023/nonmatchings/ov_SC03_023_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80182418[])(void); diff --git a/src/ov_SC03_024/ov_SC03_024_after.c b/src/ov_SC03_024/ov_SC03_024_after.c index aad5f7cb49..256ce2734f 100644 --- a/src/ov_SC03_024/ov_SC03_024_after.c +++ b/src/ov_SC03_024/ov_SC03_024_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_024/ov_SC03_024_jr_8012ACE0.c b/src/ov_SC03_024/ov_SC03_024_jr_8012ACE0.c index 7683044f81..9f2f8c4066 100644 --- a/src/ov_SC03_024/ov_SC03_024_jr_8012ACE0.c +++ b/src/ov_SC03_024/ov_SC03_024_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_024/ov_SC03_024_jr_80135D20.c b/src/ov_SC03_024/ov_SC03_024_jr_80135D20.c index dd0a410919..1ca098e5df 100644 --- a/src/ov_SC03_024/ov_SC03_024_jr_80135D20.c +++ b/src/ov_SC03_024/ov_SC03_024_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_024/ov_SC03_024_jr_80140608.c b/src/ov_SC03_024/ov_SC03_024_jr_80140608.c index 74f63f04cd..a8c934dc06 100644 --- a/src/ov_SC03_024/ov_SC03_024_jr_80140608.c +++ b/src/ov_SC03_024/ov_SC03_024_jr_80140608.c @@ -1825,7 +1825,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80188608[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80188608[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_024/ov_SC03_024_jr_80154C24.c b/src/ov_SC03_024/ov_SC03_024_jr_80154C24.c index 41728af2e0..51bd0b8685 100644 --- a/src/ov_SC03_024/ov_SC03_024_jr_80154C24.c +++ b/src/ov_SC03_024/ov_SC03_024_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_024/ov_SC03_024_jr_8015C32C.c b/src/ov_SC03_024/ov_SC03_024_jr_8015C32C.c index 61f8469ffb..bad0e3915c 100644 --- a/src/ov_SC03_024/ov_SC03_024_jr_8015C32C.c +++ b/src/ov_SC03_024/ov_SC03_024_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801899C4[]; + extern unsigned short D_801899C6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801899C4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801899C6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801899F4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801899F4)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_024/ov_SC03_024_jr_8016AB6C.c b/src/ov_SC03_024/ov_SC03_024_jr_8016AB6C.c index 87e06d23b5..342c9a364a 100644 --- a/src/ov_SC03_024/ov_SC03_024_jr_8016AB6C.c +++ b/src/ov_SC03_024/ov_SC03_024_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_024/nonmatchings/ov_SC03_024_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018A43C[])(void); diff --git a/src/ov_SC03_028/ov_SC03_028_after.c b/src/ov_SC03_028/ov_SC03_028_after.c index b42fbfdc5b..5e5be4205e 100644 --- a/src/ov_SC03_028/ov_SC03_028_after.c +++ b/src/ov_SC03_028/ov_SC03_028_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_028/ov_SC03_028_jr_8012ACE0.c b/src/ov_SC03_028/ov_SC03_028_jr_8012ACE0.c index 0eceec2564..e53fb97da8 100644 --- a/src/ov_SC03_028/ov_SC03_028_jr_8012ACE0.c +++ b/src/ov_SC03_028/ov_SC03_028_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_028/ov_SC03_028_jr_80135D20.c b/src/ov_SC03_028/ov_SC03_028_jr_80135D20.c index 3fd162a2a3..e60dfb2d5a 100644 --- a/src/ov_SC03_028/ov_SC03_028_jr_80135D20.c +++ b/src/ov_SC03_028/ov_SC03_028_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_028/ov_SC03_028_jr_80140608.c b/src/ov_SC03_028/ov_SC03_028_jr_80140608.c index a36c43c035..5775b41161 100644 --- a/src/ov_SC03_028/ov_SC03_028_jr_80140608.c +++ b/src/ov_SC03_028/ov_SC03_028_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018D0D4[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018D0D4[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_028/ov_SC03_028_jr_80154C24.c b/src/ov_SC03_028/ov_SC03_028_jr_80154C24.c index 0cf6e03b4b..2cbd3ad826 100644 --- a/src/ov_SC03_028/ov_SC03_028_jr_80154C24.c +++ b/src/ov_SC03_028/ov_SC03_028_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_028/ov_SC03_028_jr_8015C32C.c b/src/ov_SC03_028/ov_SC03_028_jr_8015C32C.c index 97ea450981..b857a482bf 100644 --- a/src/ov_SC03_028/ov_SC03_028_jr_8015C32C.c +++ b/src/ov_SC03_028/ov_SC03_028_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018E490[]; + extern unsigned short D_8018E492[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018E490[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018E492[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018E4C0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018E4C0)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_028/ov_SC03_028_jr_8016AB6C.c b/src/ov_SC03_028/ov_SC03_028_jr_8016AB6C.c index 08e1ba50c3..47543767be 100644 --- a/src/ov_SC03_028/ov_SC03_028_jr_8016AB6C.c +++ b/src/ov_SC03_028/ov_SC03_028_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_028/nonmatchings/ov_SC03_028_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018EF08[])(void); diff --git a/src/ov_SC03_029/ov_SC03_029_after.c b/src/ov_SC03_029/ov_SC03_029_after.c index dab12297fa..8138784e1f 100644 --- a/src/ov_SC03_029/ov_SC03_029_after.c +++ b/src/ov_SC03_029/ov_SC03_029_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_029/ov_SC03_029_jr_8012ACE0.c b/src/ov_SC03_029/ov_SC03_029_jr_8012ACE0.c index 85fef6a71d..55601d6a54 100644 --- a/src/ov_SC03_029/ov_SC03_029_jr_8012ACE0.c +++ b/src/ov_SC03_029/ov_SC03_029_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_029/ov_SC03_029_jr_80135D20.c b/src/ov_SC03_029/ov_SC03_029_jr_80135D20.c index fddcb0abc6..9742d545e3 100644 --- a/src/ov_SC03_029/ov_SC03_029_jr_80135D20.c +++ b/src/ov_SC03_029/ov_SC03_029_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_029/ov_SC03_029_jr_80140608.c b/src/ov_SC03_029/ov_SC03_029_jr_80140608.c index c7ec6b9576..8b46708300 100644 --- a/src/ov_SC03_029/ov_SC03_029_jr_80140608.c +++ b/src/ov_SC03_029/ov_SC03_029_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80188F0C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80188F0C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_029/ov_SC03_029_jr_80154C24.c b/src/ov_SC03_029/ov_SC03_029_jr_80154C24.c index 3ccc8fcd81..915eceb480 100644 --- a/src/ov_SC03_029/ov_SC03_029_jr_80154C24.c +++ b/src/ov_SC03_029/ov_SC03_029_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_029/ov_SC03_029_jr_8015C32C.c b/src/ov_SC03_029/ov_SC03_029_jr_8015C32C.c index 1f39a0a360..cf914d80ef 100644 --- a/src/ov_SC03_029/ov_SC03_029_jr_8015C32C.c +++ b/src/ov_SC03_029/ov_SC03_029_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018A2C8[]; + extern unsigned short D_8018A2CA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018A2C8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018A2CA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018A2F8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018A2F8)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_029/ov_SC03_029_jr_8016AB6C.c b/src/ov_SC03_029/ov_SC03_029_jr_8016AB6C.c index 5f9873f422..36a1ed9f5d 100644 --- a/src/ov_SC03_029/ov_SC03_029_jr_8016AB6C.c +++ b/src/ov_SC03_029/ov_SC03_029_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_029/nonmatchings/ov_SC03_029_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018AD40[])(void); diff --git a/src/ov_SC03_030/ov_SC03_030_after.c b/src/ov_SC03_030/ov_SC03_030_after.c index 9cecf5c912..fa3d17bcba 100644 --- a/src/ov_SC03_030/ov_SC03_030_after.c +++ b/src/ov_SC03_030/ov_SC03_030_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_030/ov_SC03_030_jr_8012ACE0.c b/src/ov_SC03_030/ov_SC03_030_jr_8012ACE0.c index 0c9750cecd..5934f85b01 100644 --- a/src/ov_SC03_030/ov_SC03_030_jr_8012ACE0.c +++ b/src/ov_SC03_030/ov_SC03_030_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_030/ov_SC03_030_jr_80135D20.c b/src/ov_SC03_030/ov_SC03_030_jr_80135D20.c index 4d1953e94a..49e210ec7f 100644 --- a/src/ov_SC03_030/ov_SC03_030_jr_80135D20.c +++ b/src/ov_SC03_030/ov_SC03_030_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_030/ov_SC03_030_jr_80140608.c b/src/ov_SC03_030/ov_SC03_030_jr_80140608.c index 809bf0d106..3ed8e1dfc3 100644 --- a/src/ov_SC03_030/ov_SC03_030_jr_80140608.c +++ b/src/ov_SC03_030/ov_SC03_030_jr_80140608.c @@ -1859,7 +1859,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183548[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183548[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_030/ov_SC03_030_jr_80154C24.c b/src/ov_SC03_030/ov_SC03_030_jr_80154C24.c index e5e12fc83b..7f69fedd02 100644 --- a/src/ov_SC03_030/ov_SC03_030_jr_80154C24.c +++ b/src/ov_SC03_030/ov_SC03_030_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_030/ov_SC03_030_jr_8015C32C.c b/src/ov_SC03_030/ov_SC03_030_jr_8015C32C.c index 743cc4b665..5b5734fb3d 100644 --- a/src/ov_SC03_030/ov_SC03_030_jr_8015C32C.c +++ b/src/ov_SC03_030/ov_SC03_030_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184904[]; + extern unsigned short D_80184906[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184904[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184906[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184934; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184934)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_030/ov_SC03_030_jr_8016AB6C.c b/src/ov_SC03_030/ov_SC03_030_jr_8016AB6C.c index bfb9ffa718..be37c079ad 100644 --- a/src/ov_SC03_030/ov_SC03_030_jr_8016AB6C.c +++ b/src/ov_SC03_030/ov_SC03_030_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_030/nonmatchings/ov_SC03_030_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018537C[])(void); diff --git a/src/ov_SC03_031/ov_SC03_031_after.c b/src/ov_SC03_031/ov_SC03_031_after.c index 1d2dce0366..4831a62304 100644 --- a/src/ov_SC03_031/ov_SC03_031_after.c +++ b/src/ov_SC03_031/ov_SC03_031_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_031/ov_SC03_031_jr_8012ACE0.c b/src/ov_SC03_031/ov_SC03_031_jr_8012ACE0.c index b1f33c30a8..7759c2e6ea 100644 --- a/src/ov_SC03_031/ov_SC03_031_jr_8012ACE0.c +++ b/src/ov_SC03_031/ov_SC03_031_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_031/ov_SC03_031_jr_80135D20.c b/src/ov_SC03_031/ov_SC03_031_jr_80135D20.c index da470ed051..4f3aff9207 100644 --- a/src/ov_SC03_031/ov_SC03_031_jr_80135D20.c +++ b/src/ov_SC03_031/ov_SC03_031_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_031/ov_SC03_031_jr_80140608.c b/src/ov_SC03_031/ov_SC03_031_jr_80140608.c index ba0a5bc538..8d7dd64a0b 100644 --- a/src/ov_SC03_031/ov_SC03_031_jr_80140608.c +++ b/src/ov_SC03_031/ov_SC03_031_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183A64[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183A64[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_031/ov_SC03_031_jr_80154C24.c b/src/ov_SC03_031/ov_SC03_031_jr_80154C24.c index 43ac45b3c8..e83f40cf53 100644 --- a/src/ov_SC03_031/ov_SC03_031_jr_80154C24.c +++ b/src/ov_SC03_031/ov_SC03_031_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_031/ov_SC03_031_jr_8015C32C.c b/src/ov_SC03_031/ov_SC03_031_jr_8015C32C.c index af5bf37a04..6bd519efba 100644 --- a/src/ov_SC03_031/ov_SC03_031_jr_8015C32C.c +++ b/src/ov_SC03_031/ov_SC03_031_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184E20[]; + extern unsigned short D_80184E22[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184E20[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184E22[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184E50; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184E50)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_031/ov_SC03_031_jr_8016AB6C.c b/src/ov_SC03_031/ov_SC03_031_jr_8016AB6C.c index f15b662af8..45c7590dc1 100644 --- a/src/ov_SC03_031/ov_SC03_031_jr_8016AB6C.c +++ b/src/ov_SC03_031/ov_SC03_031_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_031/nonmatchings/ov_SC03_031_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80185898[])(void); diff --git a/src/ov_SC03_089/ov_SC03_089_after.c b/src/ov_SC03_089/ov_SC03_089_after.c index a8136da3bb..31de64d8ac 100644 --- a/src/ov_SC03_089/ov_SC03_089_after.c +++ b/src/ov_SC03_089/ov_SC03_089_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_089/ov_SC03_089_jr_8012ACE0.c b/src/ov_SC03_089/ov_SC03_089_jr_8012ACE0.c index f1f7f657cc..f554e48ebd 100644 --- a/src/ov_SC03_089/ov_SC03_089_jr_8012ACE0.c +++ b/src/ov_SC03_089/ov_SC03_089_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_089/ov_SC03_089_jr_80135D20.c b/src/ov_SC03_089/ov_SC03_089_jr_80135D20.c index 8d1cafe957..02ccf88673 100644 --- a/src/ov_SC03_089/ov_SC03_089_jr_80135D20.c +++ b/src/ov_SC03_089/ov_SC03_089_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_089/ov_SC03_089_jr_80140608.c b/src/ov_SC03_089/ov_SC03_089_jr_80140608.c index e032e64db9..e6e4252264 100644 --- a/src/ov_SC03_089/ov_SC03_089_jr_80140608.c +++ b/src/ov_SC03_089/ov_SC03_089_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018AD60[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018AD60[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_089/ov_SC03_089_jr_80154C24.c b/src/ov_SC03_089/ov_SC03_089_jr_80154C24.c index 99dee195da..e7e04fae53 100644 --- a/src/ov_SC03_089/ov_SC03_089_jr_80154C24.c +++ b/src/ov_SC03_089/ov_SC03_089_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_089/ov_SC03_089_jr_8015C32C.c b/src/ov_SC03_089/ov_SC03_089_jr_8015C32C.c index 9537ef7b68..6b9ec66a90 100644 --- a/src/ov_SC03_089/ov_SC03_089_jr_8015C32C.c +++ b/src/ov_SC03_089/ov_SC03_089_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018C11C[]; + extern unsigned short D_8018C11E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018C11C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018C11E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018C14C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018C14C)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_089/ov_SC03_089_jr_8016AB6C.c b/src/ov_SC03_089/ov_SC03_089_jr_8016AB6C.c index 577cc7c639..caae83699c 100644 --- a/src/ov_SC03_089/ov_SC03_089_jr_8016AB6C.c +++ b/src/ov_SC03_089/ov_SC03_089_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_089/nonmatchings/ov_SC03_089_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018CB94[])(void); diff --git a/src/ov_SC03_090/ov_SC03_090_after.c b/src/ov_SC03_090/ov_SC03_090_after.c index 90970a642b..6af665e03a 100644 --- a/src/ov_SC03_090/ov_SC03_090_after.c +++ b/src/ov_SC03_090/ov_SC03_090_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_090/ov_SC03_090_jr_8012ACE0.c b/src/ov_SC03_090/ov_SC03_090_jr_8012ACE0.c index ca07cc4c24..1eb1a8a2a2 100644 --- a/src/ov_SC03_090/ov_SC03_090_jr_8012ACE0.c +++ b/src/ov_SC03_090/ov_SC03_090_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_090/ov_SC03_090_jr_80135D20.c b/src/ov_SC03_090/ov_SC03_090_jr_80135D20.c index eb24f25a91..aa3a508eec 100644 --- a/src/ov_SC03_090/ov_SC03_090_jr_80135D20.c +++ b/src/ov_SC03_090/ov_SC03_090_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_090/ov_SC03_090_jr_80140608.c b/src/ov_SC03_090/ov_SC03_090_jr_80140608.c index a595f79894..b7d344e30f 100644 --- a/src/ov_SC03_090/ov_SC03_090_jr_80140608.c +++ b/src/ov_SC03_090/ov_SC03_090_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018D184[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018D184[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_090/ov_SC03_090_jr_80154C24.c b/src/ov_SC03_090/ov_SC03_090_jr_80154C24.c index 0d9d7bb0c1..863942f8d0 100644 --- a/src/ov_SC03_090/ov_SC03_090_jr_80154C24.c +++ b/src/ov_SC03_090/ov_SC03_090_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_090/ov_SC03_090_jr_8015C32C.c b/src/ov_SC03_090/ov_SC03_090_jr_8015C32C.c index 03738627f5..cdd37c0a0f 100644 --- a/src/ov_SC03_090/ov_SC03_090_jr_8015C32C.c +++ b/src/ov_SC03_090/ov_SC03_090_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018E540[]; + extern unsigned short D_8018E542[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018E540[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018E542[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018E570; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018E570)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_090/ov_SC03_090_jr_8016AB6C.c b/src/ov_SC03_090/ov_SC03_090_jr_8016AB6C.c index a3b5a2afc3..b28660f095 100644 --- a/src/ov_SC03_090/ov_SC03_090_jr_8016AB6C.c +++ b/src/ov_SC03_090/ov_SC03_090_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_090/nonmatchings/ov_SC03_090_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018EFB8[])(void); diff --git a/src/ov_SC03_091/ov_SC03_091_after.c b/src/ov_SC03_091/ov_SC03_091_after.c index b27b60a902..ae33f79a45 100644 --- a/src/ov_SC03_091/ov_SC03_091_after.c +++ b/src/ov_SC03_091/ov_SC03_091_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_091/ov_SC03_091_jr_8012ACE0.c b/src/ov_SC03_091/ov_SC03_091_jr_8012ACE0.c index e36de8be66..80a3265bf5 100644 --- a/src/ov_SC03_091/ov_SC03_091_jr_8012ACE0.c +++ b/src/ov_SC03_091/ov_SC03_091_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_091/ov_SC03_091_jr_80135D20.c b/src/ov_SC03_091/ov_SC03_091_jr_80135D20.c index b9b42c861e..bf82c853f4 100644 --- a/src/ov_SC03_091/ov_SC03_091_jr_80135D20.c +++ b/src/ov_SC03_091/ov_SC03_091_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_091/ov_SC03_091_jr_80140608.c b/src/ov_SC03_091/ov_SC03_091_jr_80140608.c index d6fc7ebb2f..ca40b229ec 100644 --- a/src/ov_SC03_091/ov_SC03_091_jr_80140608.c +++ b/src/ov_SC03_091/ov_SC03_091_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018E02C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018E02C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_091/ov_SC03_091_jr_80154C24.c b/src/ov_SC03_091/ov_SC03_091_jr_80154C24.c index 91549846aa..d7423505f2 100644 --- a/src/ov_SC03_091/ov_SC03_091_jr_80154C24.c +++ b/src/ov_SC03_091/ov_SC03_091_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_091/ov_SC03_091_jr_8015C32C.c b/src/ov_SC03_091/ov_SC03_091_jr_8015C32C.c index 88914e4f8a..d35b5170b1 100644 --- a/src/ov_SC03_091/ov_SC03_091_jr_8015C32C.c +++ b/src/ov_SC03_091/ov_SC03_091_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018F3E8[]; + extern unsigned short D_8018F3EA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018F3E8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018F3EA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018F418; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018F418)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_091/ov_SC03_091_jr_8016AB6C.c b/src/ov_SC03_091/ov_SC03_091_jr_8016AB6C.c index b6e9fc9af4..02c6df77ad 100644 --- a/src/ov_SC03_091/ov_SC03_091_jr_8016AB6C.c +++ b/src/ov_SC03_091/ov_SC03_091_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_091/nonmatchings/ov_SC03_091_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018FE60[])(void); diff --git a/src/ov_SC03_092/ov_SC03_092_after.c b/src/ov_SC03_092/ov_SC03_092_after.c index a59b3b0dba..5b44bb566a 100644 --- a/src/ov_SC03_092/ov_SC03_092_after.c +++ b/src/ov_SC03_092/ov_SC03_092_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_092/ov_SC03_092_jr_8012ACE0.c b/src/ov_SC03_092/ov_SC03_092_jr_8012ACE0.c index e7018be6d3..2d858f7390 100644 --- a/src/ov_SC03_092/ov_SC03_092_jr_8012ACE0.c +++ b/src/ov_SC03_092/ov_SC03_092_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_092/ov_SC03_092_jr_80135D20.c b/src/ov_SC03_092/ov_SC03_092_jr_80135D20.c index 99b9950288..f2f5002119 100644 --- a/src/ov_SC03_092/ov_SC03_092_jr_80135D20.c +++ b/src/ov_SC03_092/ov_SC03_092_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_092/ov_SC03_092_jr_80140608.c b/src/ov_SC03_092/ov_SC03_092_jr_80140608.c index f0b428e9a1..746ff7b558 100644 --- a/src/ov_SC03_092/ov_SC03_092_jr_80140608.c +++ b/src/ov_SC03_092/ov_SC03_092_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801863FC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801863FC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_092/ov_SC03_092_jr_80154C24.c b/src/ov_SC03_092/ov_SC03_092_jr_80154C24.c index a4ed7d7a54..a765ec8e63 100644 --- a/src/ov_SC03_092/ov_SC03_092_jr_80154C24.c +++ b/src/ov_SC03_092/ov_SC03_092_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_092/ov_SC03_092_jr_8015C32C.c b/src/ov_SC03_092/ov_SC03_092_jr_8015C32C.c index af874fa42b..514bd98ea3 100644 --- a/src/ov_SC03_092/ov_SC03_092_jr_8015C32C.c +++ b/src/ov_SC03_092/ov_SC03_092_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801877B8[]; + extern unsigned short D_801877BA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801877B8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801877BA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801877E8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801877E8)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_092/ov_SC03_092_jr_8016AB6C.c b/src/ov_SC03_092/ov_SC03_092_jr_8016AB6C.c index e7246b2190..9487f2adc9 100644 --- a/src/ov_SC03_092/ov_SC03_092_jr_8016AB6C.c +++ b/src/ov_SC03_092/ov_SC03_092_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_092/nonmatchings/ov_SC03_092_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80188230[])(void); diff --git a/src/ov_SC03_093/ov_SC03_093_after.c b/src/ov_SC03_093/ov_SC03_093_after.c index 3bdb04cc78..a23744778f 100644 --- a/src/ov_SC03_093/ov_SC03_093_after.c +++ b/src/ov_SC03_093/ov_SC03_093_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_093/ov_SC03_093_jr_8012ACE0.c b/src/ov_SC03_093/ov_SC03_093_jr_8012ACE0.c index 31c6305487..4a9063fdad 100644 --- a/src/ov_SC03_093/ov_SC03_093_jr_8012ACE0.c +++ b/src/ov_SC03_093/ov_SC03_093_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_093/ov_SC03_093_jr_80135D20.c b/src/ov_SC03_093/ov_SC03_093_jr_80135D20.c index 2bb279feff..8980f17937 100644 --- a/src/ov_SC03_093/ov_SC03_093_jr_80135D20.c +++ b/src/ov_SC03_093/ov_SC03_093_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_093/ov_SC03_093_jr_80140608.c b/src/ov_SC03_093/ov_SC03_093_jr_80140608.c index 91517d2968..a88e9d2a0a 100644 --- a/src/ov_SC03_093/ov_SC03_093_jr_80140608.c +++ b/src/ov_SC03_093/ov_SC03_093_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801875D4[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801875D4[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_093/ov_SC03_093_jr_80154C24.c b/src/ov_SC03_093/ov_SC03_093_jr_80154C24.c index 1e4986dcb8..a92e52ca95 100644 --- a/src/ov_SC03_093/ov_SC03_093_jr_80154C24.c +++ b/src/ov_SC03_093/ov_SC03_093_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_093/ov_SC03_093_jr_8015C32C.c b/src/ov_SC03_093/ov_SC03_093_jr_8015C32C.c index d9bf00f1a8..f152d949a9 100644 --- a/src/ov_SC03_093/ov_SC03_093_jr_8015C32C.c +++ b/src/ov_SC03_093/ov_SC03_093_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80188990[]; + extern unsigned short D_80188992[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80188990[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80188992[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801889C0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801889C0)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_093/ov_SC03_093_jr_8016AB6C.c b/src/ov_SC03_093/ov_SC03_093_jr_8016AB6C.c index e54d8ae187..270973f0e9 100644 --- a/src/ov_SC03_093/ov_SC03_093_jr_8016AB6C.c +++ b/src/ov_SC03_093/ov_SC03_093_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_093/nonmatchings/ov_SC03_093_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80189408[])(void); diff --git a/src/ov_SC03_094/ov_SC03_094_after.c b/src/ov_SC03_094/ov_SC03_094_after.c index 79e3dba79a..46124cf412 100644 --- a/src/ov_SC03_094/ov_SC03_094_after.c +++ b/src/ov_SC03_094/ov_SC03_094_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_094/ov_SC03_094_jr_8012ACE0.c b/src/ov_SC03_094/ov_SC03_094_jr_8012ACE0.c index 46bc7d0e9d..838e38e071 100644 --- a/src/ov_SC03_094/ov_SC03_094_jr_8012ACE0.c +++ b/src/ov_SC03_094/ov_SC03_094_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_094/ov_SC03_094_jr_80135D20.c b/src/ov_SC03_094/ov_SC03_094_jr_80135D20.c index cbaae93888..b1f066a8dc 100644 --- a/src/ov_SC03_094/ov_SC03_094_jr_80135D20.c +++ b/src/ov_SC03_094/ov_SC03_094_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_094/ov_SC03_094_jr_80140608.c b/src/ov_SC03_094/ov_SC03_094_jr_80140608.c index 6dbde4d0de..3d138d0afb 100644 --- a/src/ov_SC03_094/ov_SC03_094_jr_80140608.c +++ b/src/ov_SC03_094/ov_SC03_094_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80188FD0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80188FD0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_094/ov_SC03_094_jr_80154C24.c b/src/ov_SC03_094/ov_SC03_094_jr_80154C24.c index c5779e0f2f..66f7e4f7ff 100644 --- a/src/ov_SC03_094/ov_SC03_094_jr_80154C24.c +++ b/src/ov_SC03_094/ov_SC03_094_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_094/ov_SC03_094_jr_8015C32C.c b/src/ov_SC03_094/ov_SC03_094_jr_8015C32C.c index fff43e111f..c03578c954 100644 --- a/src/ov_SC03_094/ov_SC03_094_jr_8015C32C.c +++ b/src/ov_SC03_094/ov_SC03_094_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018A38C[]; + extern unsigned short D_8018A38E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018A38C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018A38E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018A3BC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018A3BC)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_094/ov_SC03_094_jr_8016AB6C.c b/src/ov_SC03_094/ov_SC03_094_jr_8016AB6C.c index a974eddd67..e45a017638 100644 --- a/src/ov_SC03_094/ov_SC03_094_jr_8016AB6C.c +++ b/src/ov_SC03_094/ov_SC03_094_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_094/nonmatchings/ov_SC03_094_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018AE04[])(void); diff --git a/src/ov_SC03_095/ov_SC03_095_after.c b/src/ov_SC03_095/ov_SC03_095_after.c index ff504966b1..7aebfb298b 100644 --- a/src/ov_SC03_095/ov_SC03_095_after.c +++ b/src/ov_SC03_095/ov_SC03_095_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_095/ov_SC03_095_jr_8012ACE0.c b/src/ov_SC03_095/ov_SC03_095_jr_8012ACE0.c index bdbaffda96..6fe6fbe2fe 100644 --- a/src/ov_SC03_095/ov_SC03_095_jr_8012ACE0.c +++ b/src/ov_SC03_095/ov_SC03_095_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_095/ov_SC03_095_jr_80135D20.c b/src/ov_SC03_095/ov_SC03_095_jr_80135D20.c index d8ce2acc17..d941753783 100644 --- a/src/ov_SC03_095/ov_SC03_095_jr_80135D20.c +++ b/src/ov_SC03_095/ov_SC03_095_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_095/ov_SC03_095_jr_80140608.c b/src/ov_SC03_095/ov_SC03_095_jr_80140608.c index f8955c16df..c7e0b918fe 100644 --- a/src/ov_SC03_095/ov_SC03_095_jr_80140608.c +++ b/src/ov_SC03_095/ov_SC03_095_jr_80140608.c @@ -1825,7 +1825,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80182630[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80182630[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_095/ov_SC03_095_jr_80154C24.c b/src/ov_SC03_095/ov_SC03_095_jr_80154C24.c index e62ed5a76f..1535b588d5 100644 --- a/src/ov_SC03_095/ov_SC03_095_jr_80154C24.c +++ b/src/ov_SC03_095/ov_SC03_095_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_095/ov_SC03_095_jr_8015C32C.c b/src/ov_SC03_095/ov_SC03_095_jr_8015C32C.c index 4625adcaff..4a415e536a 100644 --- a/src/ov_SC03_095/ov_SC03_095_jr_8015C32C.c +++ b/src/ov_SC03_095/ov_SC03_095_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801839EC[]; + extern unsigned short D_801839EE[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801839EC[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801839EE[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80183A1C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80183A1C)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_095/ov_SC03_095_jr_8016AB6C.c b/src/ov_SC03_095/ov_SC03_095_jr_8016AB6C.c index 3643411d26..923cba521e 100644 --- a/src/ov_SC03_095/ov_SC03_095_jr_8016AB6C.c +++ b/src/ov_SC03_095/ov_SC03_095_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_095/nonmatchings/ov_SC03_095_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80184464[])(void); diff --git a/src/ov_SC03_096/ov_SC03_096_after.c b/src/ov_SC03_096/ov_SC03_096_after.c index 37bd306596..b9865cc747 100644 --- a/src/ov_SC03_096/ov_SC03_096_after.c +++ b/src/ov_SC03_096/ov_SC03_096_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_096/ov_SC03_096_jr_8012ACE0.c b/src/ov_SC03_096/ov_SC03_096_jr_8012ACE0.c index 8ddf8db0a1..1c4dd758fb 100644 --- a/src/ov_SC03_096/ov_SC03_096_jr_8012ACE0.c +++ b/src/ov_SC03_096/ov_SC03_096_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_096/ov_SC03_096_jr_80135D20.c b/src/ov_SC03_096/ov_SC03_096_jr_80135D20.c index 88d8ffebea..0f3276a37c 100644 --- a/src/ov_SC03_096/ov_SC03_096_jr_80135D20.c +++ b/src/ov_SC03_096/ov_SC03_096_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_096/ov_SC03_096_jr_80140608.c b/src/ov_SC03_096/ov_SC03_096_jr_80140608.c index 24ed1a4b61..2b376a19b6 100644 --- a/src/ov_SC03_096/ov_SC03_096_jr_80140608.c +++ b/src/ov_SC03_096/ov_SC03_096_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801821B0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801821B0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_096/ov_SC03_096_jr_80154C24.c b/src/ov_SC03_096/ov_SC03_096_jr_80154C24.c index 60f0c45590..4532a8675c 100644 --- a/src/ov_SC03_096/ov_SC03_096_jr_80154C24.c +++ b/src/ov_SC03_096/ov_SC03_096_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_096/ov_SC03_096_jr_8015C32C.c b/src/ov_SC03_096/ov_SC03_096_jr_8015C32C.c index 44c3b5bb04..d3ecc6bf1d 100644 --- a/src/ov_SC03_096/ov_SC03_096_jr_8015C32C.c +++ b/src/ov_SC03_096/ov_SC03_096_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018356C[]; + extern unsigned short D_8018356E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018356C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018356E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018359C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018359C)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_096/ov_SC03_096_jr_8016AB6C.c b/src/ov_SC03_096/ov_SC03_096_jr_8016AB6C.c index 27816ba6c6..8786911d68 100644 --- a/src/ov_SC03_096/ov_SC03_096_jr_8016AB6C.c +++ b/src/ov_SC03_096/ov_SC03_096_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_096/nonmatchings/ov_SC03_096_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80183FE4[])(void); diff --git a/src/ov_SC03_097/ov_SC03_097_after.c b/src/ov_SC03_097/ov_SC03_097_after.c index 0f6bb99acc..c50fd681c3 100644 --- a/src/ov_SC03_097/ov_SC03_097_after.c +++ b/src/ov_SC03_097/ov_SC03_097_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_097/ov_SC03_097_jr_8012ACE0.c b/src/ov_SC03_097/ov_SC03_097_jr_8012ACE0.c index 1992545b2a..d826214798 100644 --- a/src/ov_SC03_097/ov_SC03_097_jr_8012ACE0.c +++ b/src/ov_SC03_097/ov_SC03_097_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_097/ov_SC03_097_jr_80135D20.c b/src/ov_SC03_097/ov_SC03_097_jr_80135D20.c index 95689540ba..ac22ccd56a 100644 --- a/src/ov_SC03_097/ov_SC03_097_jr_80135D20.c +++ b/src/ov_SC03_097/ov_SC03_097_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_097/ov_SC03_097_jr_80140608.c b/src/ov_SC03_097/ov_SC03_097_jr_80140608.c index 0428ea5543..0764b57bf9 100644 --- a/src/ov_SC03_097/ov_SC03_097_jr_80140608.c +++ b/src/ov_SC03_097/ov_SC03_097_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80187590[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80187590[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_097/ov_SC03_097_jr_80154C24.c b/src/ov_SC03_097/ov_SC03_097_jr_80154C24.c index 3bbad21e24..d32759db5a 100644 --- a/src/ov_SC03_097/ov_SC03_097_jr_80154C24.c +++ b/src/ov_SC03_097/ov_SC03_097_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_097/ov_SC03_097_jr_8015C32C.c b/src/ov_SC03_097/ov_SC03_097_jr_8015C32C.c index 08bcdeedf6..a676936466 100644 --- a/src/ov_SC03_097/ov_SC03_097_jr_8015C32C.c +++ b/src/ov_SC03_097/ov_SC03_097_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018894C[]; + extern unsigned short D_8018894E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018894C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018894E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018897C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018897C)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_097/ov_SC03_097_jr_8016AB6C.c b/src/ov_SC03_097/ov_SC03_097_jr_8016AB6C.c index 26a7c8d6d6..5bdb005bbc 100644 --- a/src/ov_SC03_097/ov_SC03_097_jr_8016AB6C.c +++ b/src/ov_SC03_097/ov_SC03_097_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_097/nonmatchings/ov_SC03_097_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801893C4[])(void); diff --git a/src/ov_SC03_098/ov_SC03_098_after.c b/src/ov_SC03_098/ov_SC03_098_after.c index 66089e28f3..7a23fadfa8 100644 --- a/src/ov_SC03_098/ov_SC03_098_after.c +++ b/src/ov_SC03_098/ov_SC03_098_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_098/ov_SC03_098_jr_8012ACE0.c b/src/ov_SC03_098/ov_SC03_098_jr_8012ACE0.c index f3ad93882a..e4a0886fdb 100644 --- a/src/ov_SC03_098/ov_SC03_098_jr_8012ACE0.c +++ b/src/ov_SC03_098/ov_SC03_098_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_098/ov_SC03_098_jr_80135D20.c b/src/ov_SC03_098/ov_SC03_098_jr_80135D20.c index 5b131284be..c4c8f17e68 100644 --- a/src/ov_SC03_098/ov_SC03_098_jr_80135D20.c +++ b/src/ov_SC03_098/ov_SC03_098_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_098/ov_SC03_098_jr_80140608.c b/src/ov_SC03_098/ov_SC03_098_jr_80140608.c index eb30bcad00..2c5b7f2cdb 100644 --- a/src/ov_SC03_098/ov_SC03_098_jr_80140608.c +++ b/src/ov_SC03_098/ov_SC03_098_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80186A1C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80186A1C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_098/ov_SC03_098_jr_80154C24.c b/src/ov_SC03_098/ov_SC03_098_jr_80154C24.c index 91c3a45752..22b738b790 100644 --- a/src/ov_SC03_098/ov_SC03_098_jr_80154C24.c +++ b/src/ov_SC03_098/ov_SC03_098_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_098/ov_SC03_098_jr_8015C32C.c b/src/ov_SC03_098/ov_SC03_098_jr_8015C32C.c index 88ad490680..aaef8cb5af 100644 --- a/src/ov_SC03_098/ov_SC03_098_jr_8015C32C.c +++ b/src/ov_SC03_098/ov_SC03_098_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80187DD8[]; + extern unsigned short D_80187DDA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80187DD8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80187DDA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80187E08; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80187E08)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_098/ov_SC03_098_jr_8016AB6C.c b/src/ov_SC03_098/ov_SC03_098_jr_8016AB6C.c index 3efa55e5cd..76ec31515c 100644 --- a/src/ov_SC03_098/ov_SC03_098_jr_8016AB6C.c +++ b/src/ov_SC03_098/ov_SC03_098_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_098/nonmatchings/ov_SC03_098_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80188850[])(void); diff --git a/src/ov_SC03_099/ov_SC03_099_after.c b/src/ov_SC03_099/ov_SC03_099_after.c index 371d5895cb..64af7b3140 100644 --- a/src/ov_SC03_099/ov_SC03_099_after.c +++ b/src/ov_SC03_099/ov_SC03_099_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_099/ov_SC03_099_jr_8012ACE0.c b/src/ov_SC03_099/ov_SC03_099_jr_8012ACE0.c index 5bbaea477f..26a81d0f31 100644 --- a/src/ov_SC03_099/ov_SC03_099_jr_8012ACE0.c +++ b/src/ov_SC03_099/ov_SC03_099_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_099/ov_SC03_099_jr_80135D20.c b/src/ov_SC03_099/ov_SC03_099_jr_80135D20.c index 6cc49089e5..a0b2c61ee9 100644 --- a/src/ov_SC03_099/ov_SC03_099_jr_80135D20.c +++ b/src/ov_SC03_099/ov_SC03_099_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_099/ov_SC03_099_jr_80140608.c b/src/ov_SC03_099/ov_SC03_099_jr_80140608.c index b56c12d941..5d5df77346 100644 --- a/src/ov_SC03_099/ov_SC03_099_jr_80140608.c +++ b/src/ov_SC03_099/ov_SC03_099_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80184B1C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80184B1C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_099/ov_SC03_099_jr_80154C24.c b/src/ov_SC03_099/ov_SC03_099_jr_80154C24.c index 0402ca83ec..89ce2cc39b 100644 --- a/src/ov_SC03_099/ov_SC03_099_jr_80154C24.c +++ b/src/ov_SC03_099/ov_SC03_099_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c b/src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c index 476b09233a..c1cc417c48 100644 --- a/src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c +++ b/src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80185ED8[]; + extern unsigned short D_80185EDA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80185ED8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80185EDA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80185F08; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80185F08)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_099/ov_SC03_099_jr_8016AB6C.c b/src/ov_SC03_099/ov_SC03_099_jr_8016AB6C.c index f2bb369b21..005c8f12ba 100644 --- a/src/ov_SC03_099/ov_SC03_099_jr_8016AB6C.c +++ b/src/ov_SC03_099/ov_SC03_099_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_099/nonmatchings/ov_SC03_099_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80186950[])(void); diff --git a/src/ov_SC03_100/ov_SC03_100_after.c b/src/ov_SC03_100/ov_SC03_100_after.c index de21ed32f7..128c81f46f 100644 --- a/src/ov_SC03_100/ov_SC03_100_after.c +++ b/src/ov_SC03_100/ov_SC03_100_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_100/ov_SC03_100_jr_8012ACE0.c b/src/ov_SC03_100/ov_SC03_100_jr_8012ACE0.c index 29c34c27a5..5e86e78630 100644 --- a/src/ov_SC03_100/ov_SC03_100_jr_8012ACE0.c +++ b/src/ov_SC03_100/ov_SC03_100_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_100/ov_SC03_100_jr_80135D20.c b/src/ov_SC03_100/ov_SC03_100_jr_80135D20.c index b01a73debc..7a12766b6d 100644 --- a/src/ov_SC03_100/ov_SC03_100_jr_80135D20.c +++ b/src/ov_SC03_100/ov_SC03_100_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_100/ov_SC03_100_jr_80140608.c b/src/ov_SC03_100/ov_SC03_100_jr_80140608.c index 343423ca35..e2036c4d33 100644 --- a/src/ov_SC03_100/ov_SC03_100_jr_80140608.c +++ b/src/ov_SC03_100/ov_SC03_100_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80185FE8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80185FE8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_100/ov_SC03_100_jr_80154C24.c b/src/ov_SC03_100/ov_SC03_100_jr_80154C24.c index 0d6fd1ba1f..f1e2fe7d62 100644 --- a/src/ov_SC03_100/ov_SC03_100_jr_80154C24.c +++ b/src/ov_SC03_100/ov_SC03_100_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_100/ov_SC03_100_jr_8015C32C.c b/src/ov_SC03_100/ov_SC03_100_jr_8015C32C.c index 3d265a6fb4..2d019429b3 100644 --- a/src/ov_SC03_100/ov_SC03_100_jr_8015C32C.c +++ b/src/ov_SC03_100/ov_SC03_100_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801873A4[]; + extern unsigned short D_801873A6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801873A4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801873A6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801873D4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801873D4)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_100/ov_SC03_100_jr_8016AB6C.c b/src/ov_SC03_100/ov_SC03_100_jr_8016AB6C.c index 1792a0deef..55dd81f5c3 100644 --- a/src/ov_SC03_100/ov_SC03_100_jr_8016AB6C.c +++ b/src/ov_SC03_100/ov_SC03_100_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_100/nonmatchings/ov_SC03_100_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80187E1C[])(void); diff --git a/src/ov_SC03_101/ov_SC03_101_after.c b/src/ov_SC03_101/ov_SC03_101_after.c index 3dfe8dba3f..ee3b8486a1 100644 --- a/src/ov_SC03_101/ov_SC03_101_after.c +++ b/src/ov_SC03_101/ov_SC03_101_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_101/ov_SC03_101_jr_8012ACE0.c b/src/ov_SC03_101/ov_SC03_101_jr_8012ACE0.c index dcdd0749ae..fd7758b01c 100644 --- a/src/ov_SC03_101/ov_SC03_101_jr_8012ACE0.c +++ b/src/ov_SC03_101/ov_SC03_101_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_101/ov_SC03_101_jr_80135D20.c b/src/ov_SC03_101/ov_SC03_101_jr_80135D20.c index f4734a9e83..90535912d6 100644 --- a/src/ov_SC03_101/ov_SC03_101_jr_80135D20.c +++ b/src/ov_SC03_101/ov_SC03_101_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_101/ov_SC03_101_jr_80140608.c b/src/ov_SC03_101/ov_SC03_101_jr_80140608.c index 33ae763788..77e19a4f43 100644 --- a/src/ov_SC03_101/ov_SC03_101_jr_80140608.c +++ b/src/ov_SC03_101/ov_SC03_101_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80184FC4[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80184FC4[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_101/ov_SC03_101_jr_80154C24.c b/src/ov_SC03_101/ov_SC03_101_jr_80154C24.c index c9bd155d24..bbd120da72 100644 --- a/src/ov_SC03_101/ov_SC03_101_jr_80154C24.c +++ b/src/ov_SC03_101/ov_SC03_101_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_101/ov_SC03_101_jr_8015C32C.c b/src/ov_SC03_101/ov_SC03_101_jr_8015C32C.c index f546fdc9e4..b126bbdd1c 100644 --- a/src/ov_SC03_101/ov_SC03_101_jr_8015C32C.c +++ b/src/ov_SC03_101/ov_SC03_101_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80186380[]; + extern unsigned short D_80186382[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80186380[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80186382[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801863B0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801863B0)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_101/ov_SC03_101_jr_8016AB6C.c b/src/ov_SC03_101/ov_SC03_101_jr_8016AB6C.c index 0705f12874..71d3eb02ae 100644 --- a/src/ov_SC03_101/ov_SC03_101_jr_8016AB6C.c +++ b/src/ov_SC03_101/ov_SC03_101_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_101/nonmatchings/ov_SC03_101_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80186DF8[])(void); diff --git a/src/ov_SC03_102/ov_SC03_102_after.c b/src/ov_SC03_102/ov_SC03_102_after.c index b59329c7f1..b753a45850 100644 --- a/src/ov_SC03_102/ov_SC03_102_after.c +++ b/src/ov_SC03_102/ov_SC03_102_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_102/ov_SC03_102_jr_8012ACE0.c b/src/ov_SC03_102/ov_SC03_102_jr_8012ACE0.c index adc3ce2895..7b8b430e50 100644 --- a/src/ov_SC03_102/ov_SC03_102_jr_8012ACE0.c +++ b/src/ov_SC03_102/ov_SC03_102_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_102/ov_SC03_102_jr_80135D20.c b/src/ov_SC03_102/ov_SC03_102_jr_80135D20.c index f073881a07..ad457641c3 100644 --- a/src/ov_SC03_102/ov_SC03_102_jr_80135D20.c +++ b/src/ov_SC03_102/ov_SC03_102_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_102/ov_SC03_102_jr_80140608.c b/src/ov_SC03_102/ov_SC03_102_jr_80140608.c index 6e684d7a98..281658ac8f 100644 --- a/src/ov_SC03_102/ov_SC03_102_jr_80140608.c +++ b/src/ov_SC03_102/ov_SC03_102_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018787C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018787C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_102/ov_SC03_102_jr_80154C24.c b/src/ov_SC03_102/ov_SC03_102_jr_80154C24.c index e3f2faf539..0dc1439b8f 100644 --- a/src/ov_SC03_102/ov_SC03_102_jr_80154C24.c +++ b/src/ov_SC03_102/ov_SC03_102_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_102/ov_SC03_102_jr_8015C32C.c b/src/ov_SC03_102/ov_SC03_102_jr_8015C32C.c index 8fa959cdd9..1419eec261 100644 --- a/src/ov_SC03_102/ov_SC03_102_jr_8015C32C.c +++ b/src/ov_SC03_102/ov_SC03_102_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80188C38[]; + extern unsigned short D_80188C3A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80188C38[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80188C3A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80188C68; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80188C68)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_102/ov_SC03_102_jr_8016AB6C.c b/src/ov_SC03_102/ov_SC03_102_jr_8016AB6C.c index b69df52b56..727e62a32d 100644 --- a/src/ov_SC03_102/ov_SC03_102_jr_8016AB6C.c +++ b/src/ov_SC03_102/ov_SC03_102_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_102/nonmatchings/ov_SC03_102_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801896B0[])(void); diff --git a/src/ov_SC03_103/ov_SC03_103_after.c b/src/ov_SC03_103/ov_SC03_103_after.c index bfdf980de4..088d3a1e86 100644 --- a/src/ov_SC03_103/ov_SC03_103_after.c +++ b/src/ov_SC03_103/ov_SC03_103_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_103/ov_SC03_103_jr_8012ACE0.c b/src/ov_SC03_103/ov_SC03_103_jr_8012ACE0.c index 7a5a5f0ac1..756df331bc 100644 --- a/src/ov_SC03_103/ov_SC03_103_jr_8012ACE0.c +++ b/src/ov_SC03_103/ov_SC03_103_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_103/ov_SC03_103_jr_80135D20.c b/src/ov_SC03_103/ov_SC03_103_jr_80135D20.c index f466b11af6..8aba995d86 100644 --- a/src/ov_SC03_103/ov_SC03_103_jr_80135D20.c +++ b/src/ov_SC03_103/ov_SC03_103_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_103/ov_SC03_103_jr_80140608.c b/src/ov_SC03_103/ov_SC03_103_jr_80140608.c index 0069d3f97e..0b88d34975 100644 --- a/src/ov_SC03_103/ov_SC03_103_jr_80140608.c +++ b/src/ov_SC03_103/ov_SC03_103_jr_80140608.c @@ -1843,7 +1843,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018582C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018582C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_103/ov_SC03_103_jr_80154C24.c b/src/ov_SC03_103/ov_SC03_103_jr_80154C24.c index 2ba2e1a6be..19cd6d1c4a 100644 --- a/src/ov_SC03_103/ov_SC03_103_jr_80154C24.c +++ b/src/ov_SC03_103/ov_SC03_103_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_103/ov_SC03_103_jr_8015C32C.c b/src/ov_SC03_103/ov_SC03_103_jr_8015C32C.c index 59055260a4..1ddc606de7 100644 --- a/src/ov_SC03_103/ov_SC03_103_jr_8015C32C.c +++ b/src/ov_SC03_103/ov_SC03_103_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80186BE8[]; + extern unsigned short D_80186BEA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80186BE8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80186BEA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80186C18; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80186C18)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_103/ov_SC03_103_jr_8016AB6C.c b/src/ov_SC03_103/ov_SC03_103_jr_8016AB6C.c index 74207d39a1..ac6c86dc44 100644 --- a/src/ov_SC03_103/ov_SC03_103_jr_8016AB6C.c +++ b/src/ov_SC03_103/ov_SC03_103_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_103/nonmatchings/ov_SC03_103_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80187660[])(void); diff --git a/src/ov_SC03_104/ov_SC03_104_after.c b/src/ov_SC03_104/ov_SC03_104_after.c index 0cd14bac64..ffb3552ebf 100644 --- a/src/ov_SC03_104/ov_SC03_104_after.c +++ b/src/ov_SC03_104/ov_SC03_104_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_104/ov_SC03_104_jr_8012ACE0.c b/src/ov_SC03_104/ov_SC03_104_jr_8012ACE0.c index 7cc105b101..99221fa504 100644 --- a/src/ov_SC03_104/ov_SC03_104_jr_8012ACE0.c +++ b/src/ov_SC03_104/ov_SC03_104_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_104/ov_SC03_104_jr_80135D20.c b/src/ov_SC03_104/ov_SC03_104_jr_80135D20.c index 00e3dc7a73..8a98b660b4 100644 --- a/src/ov_SC03_104/ov_SC03_104_jr_80135D20.c +++ b/src/ov_SC03_104/ov_SC03_104_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_104/ov_SC03_104_jr_80140608.c b/src/ov_SC03_104/ov_SC03_104_jr_80140608.c index 4ba2731a17..c259607ae7 100644 --- a/src/ov_SC03_104/ov_SC03_104_jr_80140608.c +++ b/src/ov_SC03_104/ov_SC03_104_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018C3BC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018C3BC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_104/ov_SC03_104_jr_80154C24.c b/src/ov_SC03_104/ov_SC03_104_jr_80154C24.c index b9286d9b97..f1af5f480e 100644 --- a/src/ov_SC03_104/ov_SC03_104_jr_80154C24.c +++ b/src/ov_SC03_104/ov_SC03_104_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_104/ov_SC03_104_jr_8015C32C.c b/src/ov_SC03_104/ov_SC03_104_jr_8015C32C.c index 82644ae8ba..0ac3e8bee3 100644 --- a/src/ov_SC03_104/ov_SC03_104_jr_8015C32C.c +++ b/src/ov_SC03_104/ov_SC03_104_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018D778[]; + extern unsigned short D_8018D77A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018D778[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018D77A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018D7A8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018D7A8)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_104/ov_SC03_104_jr_8016AB6C.c b/src/ov_SC03_104/ov_SC03_104_jr_8016AB6C.c index 9d50dcddad..6fb5e8ca66 100644 --- a/src/ov_SC03_104/ov_SC03_104_jr_8016AB6C.c +++ b/src/ov_SC03_104/ov_SC03_104_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_104/nonmatchings/ov_SC03_104_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018E1F0[])(void); diff --git a/src/ov_SC03_105/ov_SC03_105_after.c b/src/ov_SC03_105/ov_SC03_105_after.c index 6bc12b84f5..c061a728d4 100644 --- a/src/ov_SC03_105/ov_SC03_105_after.c +++ b/src/ov_SC03_105/ov_SC03_105_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_105/ov_SC03_105_jr_8012ACE0.c b/src/ov_SC03_105/ov_SC03_105_jr_8012ACE0.c index 6d27434997..7782173811 100644 --- a/src/ov_SC03_105/ov_SC03_105_jr_8012ACE0.c +++ b/src/ov_SC03_105/ov_SC03_105_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_105/ov_SC03_105_jr_80135D20.c b/src/ov_SC03_105/ov_SC03_105_jr_80135D20.c index 835de872c6..f197402b5b 100644 --- a/src/ov_SC03_105/ov_SC03_105_jr_80135D20.c +++ b/src/ov_SC03_105/ov_SC03_105_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_105/ov_SC03_105_jr_80140608.c b/src/ov_SC03_105/ov_SC03_105_jr_80140608.c index c92c40ab74..3ac93e2025 100644 --- a/src/ov_SC03_105/ov_SC03_105_jr_80140608.c +++ b/src/ov_SC03_105/ov_SC03_105_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018BA44[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018BA44[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_105/ov_SC03_105_jr_80154C24.c b/src/ov_SC03_105/ov_SC03_105_jr_80154C24.c index cd49f0a7e0..9a7d7512fa 100644 --- a/src/ov_SC03_105/ov_SC03_105_jr_80154C24.c +++ b/src/ov_SC03_105/ov_SC03_105_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_105/ov_SC03_105_jr_8015C32C.c b/src/ov_SC03_105/ov_SC03_105_jr_8015C32C.c index 06eb1b7cb6..792fb4b1ec 100644 --- a/src/ov_SC03_105/ov_SC03_105_jr_8015C32C.c +++ b/src/ov_SC03_105/ov_SC03_105_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018CE00[]; + extern unsigned short D_8018CE02[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018CE00[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018CE02[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018CE30; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018CE30)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_105/ov_SC03_105_jr_8016AB6C.c b/src/ov_SC03_105/ov_SC03_105_jr_8016AB6C.c index fa487f43b0..2a41bdd67b 100644 --- a/src/ov_SC03_105/ov_SC03_105_jr_8016AB6C.c +++ b/src/ov_SC03_105/ov_SC03_105_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_105/nonmatchings/ov_SC03_105_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018D878[])(void); diff --git a/src/ov_SC03_108/ov_SC03_108_after.c b/src/ov_SC03_108/ov_SC03_108_after.c index 801164b690..22d425c5cc 100644 --- a/src/ov_SC03_108/ov_SC03_108_after.c +++ b/src/ov_SC03_108/ov_SC03_108_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_108/ov_SC03_108_jr_8012ACE0.c b/src/ov_SC03_108/ov_SC03_108_jr_8012ACE0.c index 7f49721646..1ed09ec57d 100644 --- a/src/ov_SC03_108/ov_SC03_108_jr_8012ACE0.c +++ b/src/ov_SC03_108/ov_SC03_108_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_108/ov_SC03_108_jr_80135D20.c b/src/ov_SC03_108/ov_SC03_108_jr_80135D20.c index e8aa5db45b..c250894f44 100644 --- a/src/ov_SC03_108/ov_SC03_108_jr_80135D20.c +++ b/src/ov_SC03_108/ov_SC03_108_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_108/ov_SC03_108_jr_80140608.c b/src/ov_SC03_108/ov_SC03_108_jr_80140608.c index 782ab5ccd3..eedd546de1 100644 --- a/src/ov_SC03_108/ov_SC03_108_jr_80140608.c +++ b/src/ov_SC03_108/ov_SC03_108_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018239C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018239C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_108/ov_SC03_108_jr_80154C24.c b/src/ov_SC03_108/ov_SC03_108_jr_80154C24.c index f7eb3a10cd..5925a0e70f 100644 --- a/src/ov_SC03_108/ov_SC03_108_jr_80154C24.c +++ b/src/ov_SC03_108/ov_SC03_108_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_108/ov_SC03_108_jr_8015C32C.c b/src/ov_SC03_108/ov_SC03_108_jr_8015C32C.c index 3bbb7a4f2c..6588d762f4 100644 --- a/src/ov_SC03_108/ov_SC03_108_jr_8015C32C.c +++ b/src/ov_SC03_108/ov_SC03_108_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80183758[]; + extern unsigned short D_8018375A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80183758[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018375A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80183788; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80183788)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_108/ov_SC03_108_jr_8016AB6C.c b/src/ov_SC03_108/ov_SC03_108_jr_8016AB6C.c index 8178c09197..59fee04202 100644 --- a/src/ov_SC03_108/ov_SC03_108_jr_8016AB6C.c +++ b/src/ov_SC03_108/ov_SC03_108_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3833,7 +3831,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4196,7 +4250,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4246,7 +4321,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_108/nonmatchings/ov_SC03_108_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801841D0[])(void); diff --git a/src/ov_SC03_109/ov_SC03_109_after.c b/src/ov_SC03_109/ov_SC03_109_after.c index 1658f3fb97..bb318222b5 100644 --- a/src/ov_SC03_109/ov_SC03_109_after.c +++ b/src/ov_SC03_109/ov_SC03_109_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_109/ov_SC03_109_jr_8012ACE0.c b/src/ov_SC03_109/ov_SC03_109_jr_8012ACE0.c index 0805b2cae9..748ee0dbcd 100644 --- a/src/ov_SC03_109/ov_SC03_109_jr_8012ACE0.c +++ b/src/ov_SC03_109/ov_SC03_109_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_109/ov_SC03_109_jr_80135D20.c b/src/ov_SC03_109/ov_SC03_109_jr_80135D20.c index 741144c376..ec4a57c01f 100644 --- a/src/ov_SC03_109/ov_SC03_109_jr_80135D20.c +++ b/src/ov_SC03_109/ov_SC03_109_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_109/ov_SC03_109_jr_80140608.c b/src/ov_SC03_109/ov_SC03_109_jr_80140608.c index 0eb44d86d8..e044a1ddd2 100644 --- a/src/ov_SC03_109/ov_SC03_109_jr_80140608.c +++ b/src/ov_SC03_109/ov_SC03_109_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017FA48[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017FA48[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_109/ov_SC03_109_jr_80154C24.c b/src/ov_SC03_109/ov_SC03_109_jr_80154C24.c index 6127523789..9e983b31a8 100644 --- a/src/ov_SC03_109/ov_SC03_109_jr_80154C24.c +++ b/src/ov_SC03_109/ov_SC03_109_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_109/ov_SC03_109_jr_8015C32C.c b/src/ov_SC03_109/ov_SC03_109_jr_8015C32C.c index f91bc08ead..ed0b1ee55b 100644 --- a/src/ov_SC03_109/ov_SC03_109_jr_8015C32C.c +++ b/src/ov_SC03_109/ov_SC03_109_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80180E04[]; + extern unsigned short D_80180E06[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80180E04[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80180E06[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80180E34; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80180E34)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_109/ov_SC03_109_jr_8016AB6C.c b/src/ov_SC03_109/ov_SC03_109_jr_8016AB6C.c index e31722479d..62828dc2cb 100644 --- a/src/ov_SC03_109/ov_SC03_109_jr_8016AB6C.c +++ b/src/ov_SC03_109/ov_SC03_109_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_109/nonmatchings/ov_SC03_109_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018187C[])(void); diff --git a/src/ov_SC03_110/ov_SC03_110_after.c b/src/ov_SC03_110/ov_SC03_110_after.c index 06514517b9..38228cec27 100644 --- a/src/ov_SC03_110/ov_SC03_110_after.c +++ b/src/ov_SC03_110/ov_SC03_110_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_110/ov_SC03_110_jr_8012ACE0.c b/src/ov_SC03_110/ov_SC03_110_jr_8012ACE0.c index 82131bb29a..c9d872502f 100644 --- a/src/ov_SC03_110/ov_SC03_110_jr_8012ACE0.c +++ b/src/ov_SC03_110/ov_SC03_110_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_110/ov_SC03_110_jr_80135D20.c b/src/ov_SC03_110/ov_SC03_110_jr_80135D20.c index e907687644..e63d4b3dfd 100644 --- a/src/ov_SC03_110/ov_SC03_110_jr_80135D20.c +++ b/src/ov_SC03_110/ov_SC03_110_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_110/ov_SC03_110_jr_80140608.c b/src/ov_SC03_110/ov_SC03_110_jr_80140608.c index f28df0470d..8f536e929f 100644 --- a/src/ov_SC03_110/ov_SC03_110_jr_80140608.c +++ b/src/ov_SC03_110/ov_SC03_110_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183468[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183468[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_110/ov_SC03_110_jr_80154C24.c b/src/ov_SC03_110/ov_SC03_110_jr_80154C24.c index b583d9f959..287f300205 100644 --- a/src/ov_SC03_110/ov_SC03_110_jr_80154C24.c +++ b/src/ov_SC03_110/ov_SC03_110_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_110/ov_SC03_110_jr_8015C32C.c b/src/ov_SC03_110/ov_SC03_110_jr_8015C32C.c index 99ec0fd1a5..c8e3afb02d 100644 --- a/src/ov_SC03_110/ov_SC03_110_jr_8015C32C.c +++ b/src/ov_SC03_110/ov_SC03_110_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184824[]; + extern unsigned short D_80184826[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184824[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184826[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184854; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184854)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_110/ov_SC03_110_jr_8016AB6C.c b/src/ov_SC03_110/ov_SC03_110_jr_8016AB6C.c index 0a342b34b6..b65a2b4d0b 100644 --- a/src/ov_SC03_110/ov_SC03_110_jr_8016AB6C.c +++ b/src/ov_SC03_110/ov_SC03_110_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_110/nonmatchings/ov_SC03_110_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018529C[])(void); diff --git a/src/ov_SC03_111/ov_SC03_111_after.c b/src/ov_SC03_111/ov_SC03_111_after.c index f34fdb768b..d03c89b396 100644 --- a/src/ov_SC03_111/ov_SC03_111_after.c +++ b/src/ov_SC03_111/ov_SC03_111_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_111/ov_SC03_111_jr_8012ACE0.c b/src/ov_SC03_111/ov_SC03_111_jr_8012ACE0.c index c3ece860fb..6eced7d0df 100644 --- a/src/ov_SC03_111/ov_SC03_111_jr_8012ACE0.c +++ b/src/ov_SC03_111/ov_SC03_111_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_111/ov_SC03_111_jr_80135D20.c b/src/ov_SC03_111/ov_SC03_111_jr_80135D20.c index e9284659b4..2e6af1291a 100644 --- a/src/ov_SC03_111/ov_SC03_111_jr_80135D20.c +++ b/src/ov_SC03_111/ov_SC03_111_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_111/ov_SC03_111_jr_80140608.c b/src/ov_SC03_111/ov_SC03_111_jr_80140608.c index d1284fb756..50132cf059 100644 --- a/src/ov_SC03_111/ov_SC03_111_jr_80140608.c +++ b/src/ov_SC03_111/ov_SC03_111_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018601C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018601C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_111/ov_SC03_111_jr_80154C24.c b/src/ov_SC03_111/ov_SC03_111_jr_80154C24.c index d74461d4a8..40555c64ff 100644 --- a/src/ov_SC03_111/ov_SC03_111_jr_80154C24.c +++ b/src/ov_SC03_111/ov_SC03_111_jr_80154C24.c @@ -1343,7 +1343,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_111/ov_SC03_111_jr_8015C32C.c b/src/ov_SC03_111/ov_SC03_111_jr_8015C32C.c index 0568ab2024..2f3b41c298 100644 --- a/src/ov_SC03_111/ov_SC03_111_jr_8015C32C.c +++ b/src/ov_SC03_111/ov_SC03_111_jr_8015C32C.c @@ -3289,7 +3289,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3575,7 +3626,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801873D8[]; + extern unsigned short D_801873DA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801873D8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801873DA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3593,7 +3667,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80187408; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80187408)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_111/ov_SC03_111_jr_8016AB6C.c b/src/ov_SC03_111/ov_SC03_111_jr_8016AB6C.c index a4d65412d6..49ec6eae0d 100644 --- a/src/ov_SC03_111/ov_SC03_111_jr_8016AB6C.c +++ b/src/ov_SC03_111/ov_SC03_111_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3883,7 +3881,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4246,7 +4300,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4296,7 +4371,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_111/nonmatchings/ov_SC03_111_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80187E50[])(void); diff --git a/src/ov_SC03_112/ov_SC03_112_after.c b/src/ov_SC03_112/ov_SC03_112_after.c index 43ea5aa451..1a355867f0 100644 --- a/src/ov_SC03_112/ov_SC03_112_after.c +++ b/src/ov_SC03_112/ov_SC03_112_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_112/ov_SC03_112_jr_8012ACE0.c b/src/ov_SC03_112/ov_SC03_112_jr_8012ACE0.c index c185f92ba7..8b7de65875 100644 --- a/src/ov_SC03_112/ov_SC03_112_jr_8012ACE0.c +++ b/src/ov_SC03_112/ov_SC03_112_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_112/ov_SC03_112_jr_80135D20.c b/src/ov_SC03_112/ov_SC03_112_jr_80135D20.c index d76bbb0490..8f3337c311 100644 --- a/src/ov_SC03_112/ov_SC03_112_jr_80135D20.c +++ b/src/ov_SC03_112/ov_SC03_112_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_112/ov_SC03_112_jr_80140608.c b/src/ov_SC03_112/ov_SC03_112_jr_80140608.c index 612e200beb..900926b970 100644 --- a/src/ov_SC03_112/ov_SC03_112_jr_80140608.c +++ b/src/ov_SC03_112/ov_SC03_112_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80186E24[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80186E24[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_112/ov_SC03_112_jr_80154C24.c b/src/ov_SC03_112/ov_SC03_112_jr_80154C24.c index 7892eb9762..e2f757ef0f 100644 --- a/src/ov_SC03_112/ov_SC03_112_jr_80154C24.c +++ b/src/ov_SC03_112/ov_SC03_112_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_112/ov_SC03_112_jr_8015C32C.c b/src/ov_SC03_112/ov_SC03_112_jr_8015C32C.c index 55f2e6146a..690d191ab0 100644 --- a/src/ov_SC03_112/ov_SC03_112_jr_8015C32C.c +++ b/src/ov_SC03_112/ov_SC03_112_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801881E0[]; + extern unsigned short D_801881E2[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801881E0[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801881E2[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80188210; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80188210)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_112/ov_SC03_112_jr_8016AB6C.c b/src/ov_SC03_112/ov_SC03_112_jr_8016AB6C.c index d5c2ee09a7..fedcd31f4b 100644 --- a/src/ov_SC03_112/ov_SC03_112_jr_8016AB6C.c +++ b/src/ov_SC03_112/ov_SC03_112_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80188C58[])(void); diff --git a/src/ov_SC03_113/ov_SC03_113_after.c b/src/ov_SC03_113/ov_SC03_113_after.c index 72f6e0f204..b55a878392 100644 --- a/src/ov_SC03_113/ov_SC03_113_after.c +++ b/src/ov_SC03_113/ov_SC03_113_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_113/ov_SC03_113_jr_8012ACE0.c b/src/ov_SC03_113/ov_SC03_113_jr_8012ACE0.c index 0608ba09ff..f65c9515b5 100644 --- a/src/ov_SC03_113/ov_SC03_113_jr_8012ACE0.c +++ b/src/ov_SC03_113/ov_SC03_113_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_113/ov_SC03_113_jr_80135D20.c b/src/ov_SC03_113/ov_SC03_113_jr_80135D20.c index 29c3949fbf..e85af337b4 100644 --- a/src/ov_SC03_113/ov_SC03_113_jr_80135D20.c +++ b/src/ov_SC03_113/ov_SC03_113_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_113/ov_SC03_113_jr_80140608.c b/src/ov_SC03_113/ov_SC03_113_jr_80140608.c index e93e857f46..8e2938d3eb 100644 --- a/src/ov_SC03_113/ov_SC03_113_jr_80140608.c +++ b/src/ov_SC03_113/ov_SC03_113_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801844D4[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801844D4[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_113/ov_SC03_113_jr_80154C24.c b/src/ov_SC03_113/ov_SC03_113_jr_80154C24.c index 9644cbd45e..c837b49d78 100644 --- a/src/ov_SC03_113/ov_SC03_113_jr_80154C24.c +++ b/src/ov_SC03_113/ov_SC03_113_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_113/ov_SC03_113_jr_8015C32C.c b/src/ov_SC03_113/ov_SC03_113_jr_8015C32C.c index ae0b46c7ee..703de3df04 100644 --- a/src/ov_SC03_113/ov_SC03_113_jr_8015C32C.c +++ b/src/ov_SC03_113/ov_SC03_113_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80185890[]; + extern unsigned short D_80185892[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80185890[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80185892[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801858C0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801858C0)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_113/ov_SC03_113_jr_8016AB6C.c b/src/ov_SC03_113/ov_SC03_113_jr_8016AB6C.c index 69f107f254..425516408a 100644 --- a/src/ov_SC03_113/ov_SC03_113_jr_8016AB6C.c +++ b/src/ov_SC03_113/ov_SC03_113_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_113/nonmatchings/ov_SC03_113_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80186308[])(void); diff --git a/src/ov_SC03_114/ov_SC03_114_after.c b/src/ov_SC03_114/ov_SC03_114_after.c index 83c22bdbb9..bc5235d101 100644 --- a/src/ov_SC03_114/ov_SC03_114_after.c +++ b/src/ov_SC03_114/ov_SC03_114_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_114/ov_SC03_114_jr_8012ACE0.c b/src/ov_SC03_114/ov_SC03_114_jr_8012ACE0.c index 809740fc46..6cbc84e7d5 100644 --- a/src/ov_SC03_114/ov_SC03_114_jr_8012ACE0.c +++ b/src/ov_SC03_114/ov_SC03_114_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_114/ov_SC03_114_jr_80135D20.c b/src/ov_SC03_114/ov_SC03_114_jr_80135D20.c index 7edd57e6bc..1578c25195 100644 --- a/src/ov_SC03_114/ov_SC03_114_jr_80135D20.c +++ b/src/ov_SC03_114/ov_SC03_114_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_114/ov_SC03_114_jr_80140608.c b/src/ov_SC03_114/ov_SC03_114_jr_80140608.c index f7bbcc2b0b..a0e29b6ac9 100644 --- a/src/ov_SC03_114/ov_SC03_114_jr_80140608.c +++ b/src/ov_SC03_114/ov_SC03_114_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018013C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018013C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_114/ov_SC03_114_jr_80154C24.c b/src/ov_SC03_114/ov_SC03_114_jr_80154C24.c index 444b84aa79..3deaba06e4 100644 --- a/src/ov_SC03_114/ov_SC03_114_jr_80154C24.c +++ b/src/ov_SC03_114/ov_SC03_114_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_114/ov_SC03_114_jr_8015C32C.c b/src/ov_SC03_114/ov_SC03_114_jr_8015C32C.c index 7ef70433d5..873343e236 100644 --- a/src/ov_SC03_114/ov_SC03_114_jr_8015C32C.c +++ b/src/ov_SC03_114/ov_SC03_114_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801814F8[]; + extern unsigned short D_801814FA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801814F8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801814FA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80181528; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80181528)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_114/ov_SC03_114_jr_8016AB6C.c b/src/ov_SC03_114/ov_SC03_114_jr_8016AB6C.c index 5a016457c8..a2f58df1ee 100644 --- a/src/ov_SC03_114/ov_SC03_114_jr_8016AB6C.c +++ b/src/ov_SC03_114/ov_SC03_114_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_114/nonmatchings/ov_SC03_114_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80181F70[])(void); diff --git a/src/ov_SC03_115/ov_SC03_115_after.c b/src/ov_SC03_115/ov_SC03_115_after.c index 368d8304db..7c2c110c93 100644 --- a/src/ov_SC03_115/ov_SC03_115_after.c +++ b/src/ov_SC03_115/ov_SC03_115_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_115/ov_SC03_115_jr_8012ACE0.c b/src/ov_SC03_115/ov_SC03_115_jr_8012ACE0.c index 7afdce817f..8cee2539c5 100644 --- a/src/ov_SC03_115/ov_SC03_115_jr_8012ACE0.c +++ b/src/ov_SC03_115/ov_SC03_115_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_115/ov_SC03_115_jr_80135D20.c b/src/ov_SC03_115/ov_SC03_115_jr_80135D20.c index 8d4559b42c..b80c0c6284 100644 --- a/src/ov_SC03_115/ov_SC03_115_jr_80135D20.c +++ b/src/ov_SC03_115/ov_SC03_115_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_115/ov_SC03_115_jr_80140608.c b/src/ov_SC03_115/ov_SC03_115_jr_80140608.c index 3102a36150..39c0bef8d2 100644 --- a/src/ov_SC03_115/ov_SC03_115_jr_80140608.c +++ b/src/ov_SC03_115/ov_SC03_115_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80181BA8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80181BA8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_115/ov_SC03_115_jr_80154C24.c b/src/ov_SC03_115/ov_SC03_115_jr_80154C24.c index 5793a76cce..ea4c9fbf99 100644 --- a/src/ov_SC03_115/ov_SC03_115_jr_80154C24.c +++ b/src/ov_SC03_115/ov_SC03_115_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_115/ov_SC03_115_jr_8015C32C.c b/src/ov_SC03_115/ov_SC03_115_jr_8015C32C.c index 02daeb63c8..83faaf5143 100644 --- a/src/ov_SC03_115/ov_SC03_115_jr_8015C32C.c +++ b/src/ov_SC03_115/ov_SC03_115_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80182F64[]; + extern unsigned short D_80182F66[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80182F64[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80182F66[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80182F94; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80182F94)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_115/ov_SC03_115_jr_8016AB6C.c b/src/ov_SC03_115/ov_SC03_115_jr_8016AB6C.c index fcf7d454c5..eb8a92c917 100644 --- a/src/ov_SC03_115/ov_SC03_115_jr_8016AB6C.c +++ b/src/ov_SC03_115/ov_SC03_115_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_115/nonmatchings/ov_SC03_115_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801839DC[])(void); diff --git a/src/ov_SC03_116/ov_SC03_116_after.c b/src/ov_SC03_116/ov_SC03_116_after.c index b4b872110b..3f8afaea7c 100644 --- a/src/ov_SC03_116/ov_SC03_116_after.c +++ b/src/ov_SC03_116/ov_SC03_116_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_116/ov_SC03_116_jr_8012ACE0.c b/src/ov_SC03_116/ov_SC03_116_jr_8012ACE0.c index 94861592fc..7946f14b2a 100644 --- a/src/ov_SC03_116/ov_SC03_116_jr_8012ACE0.c +++ b/src/ov_SC03_116/ov_SC03_116_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_116/ov_SC03_116_jr_80135D20.c b/src/ov_SC03_116/ov_SC03_116_jr_80135D20.c index a060d1e13c..0f6cc177d2 100644 --- a/src/ov_SC03_116/ov_SC03_116_jr_80135D20.c +++ b/src/ov_SC03_116/ov_SC03_116_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_116/ov_SC03_116_jr_80140608.c b/src/ov_SC03_116/ov_SC03_116_jr_80140608.c index 96aa406515..41a31963ce 100644 --- a/src/ov_SC03_116/ov_SC03_116_jr_80140608.c +++ b/src/ov_SC03_116/ov_SC03_116_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183FAC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183FAC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_116/ov_SC03_116_jr_80154C24.c b/src/ov_SC03_116/ov_SC03_116_jr_80154C24.c index 8da5ca74ed..1f457a47bc 100644 --- a/src/ov_SC03_116/ov_SC03_116_jr_80154C24.c +++ b/src/ov_SC03_116/ov_SC03_116_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_116/ov_SC03_116_jr_8015C32C.c b/src/ov_SC03_116/ov_SC03_116_jr_8015C32C.c index fe7aa1efb7..fdcbfb1a08 100644 --- a/src/ov_SC03_116/ov_SC03_116_jr_8015C32C.c +++ b/src/ov_SC03_116/ov_SC03_116_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80185368[]; + extern unsigned short D_8018536A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80185368[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018536A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80185398; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80185398)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_116/ov_SC03_116_jr_8016AB6C.c b/src/ov_SC03_116/ov_SC03_116_jr_8016AB6C.c index 1cd2376375..604c8035fa 100644 --- a/src/ov_SC03_116/ov_SC03_116_jr_8016AB6C.c +++ b/src/ov_SC03_116/ov_SC03_116_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80185DE0[])(void); diff --git a/src/ov_SC03_117/ov_SC03_117_after.c b/src/ov_SC03_117/ov_SC03_117_after.c index 7006749ae5..13898f964a 100644 --- a/src/ov_SC03_117/ov_SC03_117_after.c +++ b/src/ov_SC03_117/ov_SC03_117_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_117/ov_SC03_117_jr_8012ACE0.c b/src/ov_SC03_117/ov_SC03_117_jr_8012ACE0.c index ed8a7935fc..032011c992 100644 --- a/src/ov_SC03_117/ov_SC03_117_jr_8012ACE0.c +++ b/src/ov_SC03_117/ov_SC03_117_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_117/ov_SC03_117_jr_80135D20.c b/src/ov_SC03_117/ov_SC03_117_jr_80135D20.c index ffec00672b..be3b717716 100644 --- a/src/ov_SC03_117/ov_SC03_117_jr_80135D20.c +++ b/src/ov_SC03_117/ov_SC03_117_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_117/ov_SC03_117_jr_80140608.c b/src/ov_SC03_117/ov_SC03_117_jr_80140608.c index f60f37c11a..a3a5af4be8 100644 --- a/src/ov_SC03_117/ov_SC03_117_jr_80140608.c +++ b/src/ov_SC03_117/ov_SC03_117_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80185FB8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80185FB8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_117/ov_SC03_117_jr_80154C24.c b/src/ov_SC03_117/ov_SC03_117_jr_80154C24.c index f2298d6766..d7f28a29f9 100644 --- a/src/ov_SC03_117/ov_SC03_117_jr_80154C24.c +++ b/src/ov_SC03_117/ov_SC03_117_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_117/ov_SC03_117_jr_8015C32C.c b/src/ov_SC03_117/ov_SC03_117_jr_8015C32C.c index 32682fb9ce..fcc28cbd57 100644 --- a/src/ov_SC03_117/ov_SC03_117_jr_8015C32C.c +++ b/src/ov_SC03_117/ov_SC03_117_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80187374[]; + extern unsigned short D_80187376[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80187374[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80187376[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801873A4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801873A4)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_117/ov_SC03_117_jr_8016AB6C.c b/src/ov_SC03_117/ov_SC03_117_jr_8016AB6C.c index 4d1744dfb9..923013ffa8 100644 --- a/src/ov_SC03_117/ov_SC03_117_jr_8016AB6C.c +++ b/src/ov_SC03_117/ov_SC03_117_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_117/nonmatchings/ov_SC03_117_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80187DEC[])(void); diff --git a/src/ov_SC03_118/ov_SC03_118_after.c b/src/ov_SC03_118/ov_SC03_118_after.c index 7716140f9f..8ac8a5398d 100644 --- a/src/ov_SC03_118/ov_SC03_118_after.c +++ b/src/ov_SC03_118/ov_SC03_118_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_118/ov_SC03_118_jr_8012ACE0.c b/src/ov_SC03_118/ov_SC03_118_jr_8012ACE0.c index 5406c94ff6..3ef376787c 100644 --- a/src/ov_SC03_118/ov_SC03_118_jr_8012ACE0.c +++ b/src/ov_SC03_118/ov_SC03_118_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_118/ov_SC03_118_jr_80135D20.c b/src/ov_SC03_118/ov_SC03_118_jr_80135D20.c index 7a890901f6..1905eb9a04 100644 --- a/src/ov_SC03_118/ov_SC03_118_jr_80135D20.c +++ b/src/ov_SC03_118/ov_SC03_118_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_118/ov_SC03_118_jr_80140608.c b/src/ov_SC03_118/ov_SC03_118_jr_80140608.c index e301d9f20e..c6109a608a 100644 --- a/src/ov_SC03_118/ov_SC03_118_jr_80140608.c +++ b/src/ov_SC03_118/ov_SC03_118_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018B064[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018B064[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_118/ov_SC03_118_jr_80154C24.c b/src/ov_SC03_118/ov_SC03_118_jr_80154C24.c index ee680c041c..a9abd5254e 100644 --- a/src/ov_SC03_118/ov_SC03_118_jr_80154C24.c +++ b/src/ov_SC03_118/ov_SC03_118_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_118/ov_SC03_118_jr_8015C32C.c b/src/ov_SC03_118/ov_SC03_118_jr_8015C32C.c index 203e25e394..4578950b1d 100644 --- a/src/ov_SC03_118/ov_SC03_118_jr_8015C32C.c +++ b/src/ov_SC03_118/ov_SC03_118_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018C420[]; + extern unsigned short D_8018C422[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018C420[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018C422[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018C450; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018C450)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_118/ov_SC03_118_jr_8016AB6C.c b/src/ov_SC03_118/ov_SC03_118_jr_8016AB6C.c index b1b9ae5c96..69c6ae99b5 100644 --- a/src/ov_SC03_118/ov_SC03_118_jr_8016AB6C.c +++ b/src/ov_SC03_118/ov_SC03_118_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_118/nonmatchings/ov_SC03_118_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018CE98[])(void); diff --git a/src/ov_SC03_119/ov_SC03_119_after.c b/src/ov_SC03_119/ov_SC03_119_after.c index a024beb186..6431a0d607 100644 --- a/src/ov_SC03_119/ov_SC03_119_after.c +++ b/src/ov_SC03_119/ov_SC03_119_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_119/ov_SC03_119_jr_8012ACE0.c b/src/ov_SC03_119/ov_SC03_119_jr_8012ACE0.c index d57ee8e6b1..c87c502e27 100644 --- a/src/ov_SC03_119/ov_SC03_119_jr_8012ACE0.c +++ b/src/ov_SC03_119/ov_SC03_119_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_119/ov_SC03_119_jr_80135D20.c b/src/ov_SC03_119/ov_SC03_119_jr_80135D20.c index b7f40ace2d..ed89ecd3cf 100644 --- a/src/ov_SC03_119/ov_SC03_119_jr_80135D20.c +++ b/src/ov_SC03_119/ov_SC03_119_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_119/ov_SC03_119_jr_80140608.c b/src/ov_SC03_119/ov_SC03_119_jr_80140608.c index 388ebbfa17..8961c49d3a 100644 --- a/src/ov_SC03_119/ov_SC03_119_jr_80140608.c +++ b/src/ov_SC03_119/ov_SC03_119_jr_80140608.c @@ -1825,7 +1825,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018B064[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018B064[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_119/ov_SC03_119_jr_80154C24.c b/src/ov_SC03_119/ov_SC03_119_jr_80154C24.c index 0eb7335668..75a1f3f3de 100644 --- a/src/ov_SC03_119/ov_SC03_119_jr_80154C24.c +++ b/src/ov_SC03_119/ov_SC03_119_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_119/ov_SC03_119_jr_8015C32C.c b/src/ov_SC03_119/ov_SC03_119_jr_8015C32C.c index 54273cf7b0..c7b10ed379 100644 --- a/src/ov_SC03_119/ov_SC03_119_jr_8015C32C.c +++ b/src/ov_SC03_119/ov_SC03_119_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018C420[]; + extern unsigned short D_8018C422[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018C420[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018C422[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018C450; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018C450)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_119/ov_SC03_119_jr_8016AB6C.c b/src/ov_SC03_119/ov_SC03_119_jr_8016AB6C.c index 9920b9a538..3e56ce2d6b 100644 --- a/src/ov_SC03_119/ov_SC03_119_jr_8016AB6C.c +++ b/src/ov_SC03_119/ov_SC03_119_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_119/nonmatchings/ov_SC03_119_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018CE98[])(void); diff --git a/src/ov_SC03_121/ov_SC03_121_after.c b/src/ov_SC03_121/ov_SC03_121_after.c index 8c21ea391b..3c42e36047 100644 --- a/src/ov_SC03_121/ov_SC03_121_after.c +++ b/src/ov_SC03_121/ov_SC03_121_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_121/ov_SC03_121_jr_8012ACE0.c b/src/ov_SC03_121/ov_SC03_121_jr_8012ACE0.c index fc39e9457f..3e006afc57 100644 --- a/src/ov_SC03_121/ov_SC03_121_jr_8012ACE0.c +++ b/src/ov_SC03_121/ov_SC03_121_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_121/ov_SC03_121_jr_80135D20.c b/src/ov_SC03_121/ov_SC03_121_jr_80135D20.c index bd38cce24c..ca9bb68686 100644 --- a/src/ov_SC03_121/ov_SC03_121_jr_80135D20.c +++ b/src/ov_SC03_121/ov_SC03_121_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_121/ov_SC03_121_jr_80140608.c b/src/ov_SC03_121/ov_SC03_121_jr_80140608.c index 1559daf512..7f0545cd3d 100644 --- a/src/ov_SC03_121/ov_SC03_121_jr_80140608.c +++ b/src/ov_SC03_121/ov_SC03_121_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183880[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183880[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_121/ov_SC03_121_jr_80154C24.c b/src/ov_SC03_121/ov_SC03_121_jr_80154C24.c index 72bae0c33a..f040d94742 100644 --- a/src/ov_SC03_121/ov_SC03_121_jr_80154C24.c +++ b/src/ov_SC03_121/ov_SC03_121_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_121/ov_SC03_121_jr_8015C32C.c b/src/ov_SC03_121/ov_SC03_121_jr_8015C32C.c index 0edd5544d8..f14d3ed433 100644 --- a/src/ov_SC03_121/ov_SC03_121_jr_8015C32C.c +++ b/src/ov_SC03_121/ov_SC03_121_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184C3C[]; + extern unsigned short D_80184C3E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184C3C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184C3E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184C6C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184C6C)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_121/ov_SC03_121_jr_8016AB6C.c b/src/ov_SC03_121/ov_SC03_121_jr_8016AB6C.c index 3ac6da3a6e..4a5879ab8d 100644 --- a/src/ov_SC03_121/ov_SC03_121_jr_8016AB6C.c +++ b/src/ov_SC03_121/ov_SC03_121_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_121/nonmatchings/ov_SC03_121_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801856B4[])(void); diff --git a/src/ov_SC03_124/ov_SC03_124_after.c b/src/ov_SC03_124/ov_SC03_124_after.c index e21006b359..e4917ebeac 100644 --- a/src/ov_SC03_124/ov_SC03_124_after.c +++ b/src/ov_SC03_124/ov_SC03_124_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_124/ov_SC03_124_jr_8012ACE0.c b/src/ov_SC03_124/ov_SC03_124_jr_8012ACE0.c index 1e7df6aec1..ceef8b354f 100644 --- a/src/ov_SC03_124/ov_SC03_124_jr_8012ACE0.c +++ b/src/ov_SC03_124/ov_SC03_124_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_124/ov_SC03_124_jr_80135D20.c b/src/ov_SC03_124/ov_SC03_124_jr_80135D20.c index 223c8295ea..a58f663c26 100644 --- a/src/ov_SC03_124/ov_SC03_124_jr_80135D20.c +++ b/src/ov_SC03_124/ov_SC03_124_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_124/ov_SC03_124_jr_80140608.c b/src/ov_SC03_124/ov_SC03_124_jr_80140608.c index 0256b989ae..f3a10f504f 100644 --- a/src/ov_SC03_124/ov_SC03_124_jr_80140608.c +++ b/src/ov_SC03_124/ov_SC03_124_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018C2AC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018C2AC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_124/ov_SC03_124_jr_80154C24.c b/src/ov_SC03_124/ov_SC03_124_jr_80154C24.c index a2904b03b6..e575608f00 100644 --- a/src/ov_SC03_124/ov_SC03_124_jr_80154C24.c +++ b/src/ov_SC03_124/ov_SC03_124_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_124/ov_SC03_124_jr_8015C32C.c b/src/ov_SC03_124/ov_SC03_124_jr_8015C32C.c index 491278fdc9..e3319f0788 100644 --- a/src/ov_SC03_124/ov_SC03_124_jr_8015C32C.c +++ b/src/ov_SC03_124/ov_SC03_124_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018D668[]; + extern unsigned short D_8018D66A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018D668[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018D66A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018D698; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018D698)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_124/ov_SC03_124_jr_8016AB6C.c b/src/ov_SC03_124/ov_SC03_124_jr_8016AB6C.c index 9b70be6f17..6035d3ce00 100644 --- a/src/ov_SC03_124/ov_SC03_124_jr_8016AB6C.c +++ b/src/ov_SC03_124/ov_SC03_124_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_124/nonmatchings/ov_SC03_124_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018E0E0[])(void); diff --git a/src/ov_SC03_125/ov_SC03_125_after.c b/src/ov_SC03_125/ov_SC03_125_after.c index c419f7742b..eb6d84cabb 100644 --- a/src/ov_SC03_125/ov_SC03_125_after.c +++ b/src/ov_SC03_125/ov_SC03_125_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_125/ov_SC03_125_jr_8012ACE0.c b/src/ov_SC03_125/ov_SC03_125_jr_8012ACE0.c index 3018fe1fa2..f4a1e3ef89 100644 --- a/src/ov_SC03_125/ov_SC03_125_jr_8012ACE0.c +++ b/src/ov_SC03_125/ov_SC03_125_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_125/ov_SC03_125_jr_80135D20.c b/src/ov_SC03_125/ov_SC03_125_jr_80135D20.c index 6ee5c57d94..6ac0fe6879 100644 --- a/src/ov_SC03_125/ov_SC03_125_jr_80135D20.c +++ b/src/ov_SC03_125/ov_SC03_125_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_125/ov_SC03_125_jr_80140608.c b/src/ov_SC03_125/ov_SC03_125_jr_80140608.c index 249b71ed4c..8deaf6356c 100644 --- a/src/ov_SC03_125/ov_SC03_125_jr_80140608.c +++ b/src/ov_SC03_125/ov_SC03_125_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018547C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018547C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_125/ov_SC03_125_jr_80154C24.c b/src/ov_SC03_125/ov_SC03_125_jr_80154C24.c index a203b04254..278ba6ebe5 100644 --- a/src/ov_SC03_125/ov_SC03_125_jr_80154C24.c +++ b/src/ov_SC03_125/ov_SC03_125_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_125/ov_SC03_125_jr_8015C32C.c b/src/ov_SC03_125/ov_SC03_125_jr_8015C32C.c index b5a3d5c82d..81ce35b5ed 100644 --- a/src/ov_SC03_125/ov_SC03_125_jr_8015C32C.c +++ b/src/ov_SC03_125/ov_SC03_125_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80186838[]; + extern unsigned short D_8018683A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80186838[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018683A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80186868; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80186868)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_125/ov_SC03_125_jr_8016AB6C.c b/src/ov_SC03_125/ov_SC03_125_jr_8016AB6C.c index b85b41eb4a..8d2671ab84 100644 --- a/src/ov_SC03_125/ov_SC03_125_jr_8016AB6C.c +++ b/src/ov_SC03_125/ov_SC03_125_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_125/nonmatchings/ov_SC03_125_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801872B0[])(void); diff --git a/src/ov_SC03_126/ov_SC03_126_after.c b/src/ov_SC03_126/ov_SC03_126_after.c index 7b0d659bd4..8e28935d5e 100644 --- a/src/ov_SC03_126/ov_SC03_126_after.c +++ b/src/ov_SC03_126/ov_SC03_126_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC03_126/ov_SC03_126_jr_8012ACE0.c b/src/ov_SC03_126/ov_SC03_126_jr_8012ACE0.c index 91b8e623be..418e965441 100644 --- a/src/ov_SC03_126/ov_SC03_126_jr_8012ACE0.c +++ b/src/ov_SC03_126/ov_SC03_126_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC03_126/ov_SC03_126_jr_80135D20.c b/src/ov_SC03_126/ov_SC03_126_jr_80135D20.c index 3dad9f2108..fb9ff7e2ab 100644 --- a/src/ov_SC03_126/ov_SC03_126_jr_80135D20.c +++ b/src/ov_SC03_126/ov_SC03_126_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC03_126/ov_SC03_126_jr_80140608.c b/src/ov_SC03_126/ov_SC03_126_jr_80140608.c index 97d07a83c7..3985d37020 100644 --- a/src/ov_SC03_126/ov_SC03_126_jr_80140608.c +++ b/src/ov_SC03_126/ov_SC03_126_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80180974[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80180974[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC03_126/ov_SC03_126_jr_80154C24.c b/src/ov_SC03_126/ov_SC03_126_jr_80154C24.c index 6dc1569f71..91ce321110 100644 --- a/src/ov_SC03_126/ov_SC03_126_jr_80154C24.c +++ b/src/ov_SC03_126/ov_SC03_126_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC03_126/ov_SC03_126_jr_8015C32C.c b/src/ov_SC03_126/ov_SC03_126_jr_8015C32C.c index 31308a7463..9bd5809d3c 100644 --- a/src/ov_SC03_126/ov_SC03_126_jr_8015C32C.c +++ b/src/ov_SC03_126/ov_SC03_126_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80181D30[]; + extern unsigned short D_80181D32[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80181D30[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80181D32[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80181D60; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80181D60)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC03_126/ov_SC03_126_jr_8016AB6C.c b/src/ov_SC03_126/ov_SC03_126_jr_8016AB6C.c index 549f21d1a2..319399981a 100644 --- a/src/ov_SC03_126/ov_SC03_126_jr_8016AB6C.c +++ b/src/ov_SC03_126/ov_SC03_126_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC03_126/nonmatchings/ov_SC03_126_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801827A8[])(void); diff --git a/src/ov_SC04_000/ov_SC04_000_after.c b/src/ov_SC04_000/ov_SC04_000_after.c index b1674f1f02..6b935da35f 100644 --- a/src/ov_SC04_000/ov_SC04_000_after.c +++ b/src/ov_SC04_000/ov_SC04_000_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_000/ov_SC04_000_jr_8012ACE0.c b/src/ov_SC04_000/ov_SC04_000_jr_8012ACE0.c index 514609b29e..528c421dd2 100644 --- a/src/ov_SC04_000/ov_SC04_000_jr_8012ACE0.c +++ b/src/ov_SC04_000/ov_SC04_000_jr_8012ACE0.c @@ -990,7 +990,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1900,9 +2035,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_000/ov_SC04_000_jr_80135D20.c b/src/ov_SC04_000/ov_SC04_000_jr_80135D20.c index 65107c9abf..67a1523d88 100644 --- a/src/ov_SC04_000/ov_SC04_000_jr_80135D20.c +++ b/src/ov_SC04_000/ov_SC04_000_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_000/ov_SC04_000_jr_80140608.c b/src/ov_SC04_000/ov_SC04_000_jr_80140608.c index 142cd4d280..452957b036 100644 --- a/src/ov_SC04_000/ov_SC04_000_jr_80140608.c +++ b/src/ov_SC04_000/ov_SC04_000_jr_80140608.c @@ -1811,7 +1811,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80184980[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80184980[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_000/ov_SC04_000_jr_80154C24.c b/src/ov_SC04_000/ov_SC04_000_jr_80154C24.c index f714e72876..03180466c4 100644 --- a/src/ov_SC04_000/ov_SC04_000_jr_80154C24.c +++ b/src/ov_SC04_000/ov_SC04_000_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_000/ov_SC04_000_jr_8015C32C.c b/src/ov_SC04_000/ov_SC04_000_jr_8015C32C.c index 9079ff1f66..8c848c1f79 100644 --- a/src/ov_SC04_000/ov_SC04_000_jr_8015C32C.c +++ b/src/ov_SC04_000/ov_SC04_000_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80185D3C[]; + extern unsigned short D_80185D3E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80185D3C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80185D3E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80185D6C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80185D6C)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_000/ov_SC04_000_jr_8016AB6C.c b/src/ov_SC04_000/ov_SC04_000_jr_8016AB6C.c index f988ae8160..6acde24ce3 100644 --- a/src/ov_SC04_000/ov_SC04_000_jr_8016AB6C.c +++ b/src/ov_SC04_000/ov_SC04_000_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_000/nonmatchings/ov_SC04_000_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801867B4[])(void); diff --git a/src/ov_SC04_002/ov_SC04_002_after.c b/src/ov_SC04_002/ov_SC04_002_after.c index d55dea671a..d2959795f4 100644 --- a/src/ov_SC04_002/ov_SC04_002_after.c +++ b/src/ov_SC04_002/ov_SC04_002_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_002/ov_SC04_002_jr_8012ACE0.c b/src/ov_SC04_002/ov_SC04_002_jr_8012ACE0.c index f9a3bd7e30..2d88d5cb70 100644 --- a/src/ov_SC04_002/ov_SC04_002_jr_8012ACE0.c +++ b/src/ov_SC04_002/ov_SC04_002_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_002/ov_SC04_002_jr_80135D20.c b/src/ov_SC04_002/ov_SC04_002_jr_80135D20.c index 18628e1b1e..c67afc46e5 100644 --- a/src/ov_SC04_002/ov_SC04_002_jr_80135D20.c +++ b/src/ov_SC04_002/ov_SC04_002_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_002/ov_SC04_002_jr_80140608.c b/src/ov_SC04_002/ov_SC04_002_jr_80140608.c index f378602790..baa445e2e1 100644 --- a/src/ov_SC04_002/ov_SC04_002_jr_80140608.c +++ b/src/ov_SC04_002/ov_SC04_002_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80189AEC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80189AEC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_002/ov_SC04_002_jr_80154C24.c b/src/ov_SC04_002/ov_SC04_002_jr_80154C24.c index 32a623fc8a..bb20a272cb 100644 --- a/src/ov_SC04_002/ov_SC04_002_jr_80154C24.c +++ b/src/ov_SC04_002/ov_SC04_002_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_002/ov_SC04_002_jr_8015C32C.c b/src/ov_SC04_002/ov_SC04_002_jr_8015C32C.c index dbd48bbd83..2923072458 100644 --- a/src/ov_SC04_002/ov_SC04_002_jr_8015C32C.c +++ b/src/ov_SC04_002/ov_SC04_002_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018AEA8[]; + extern unsigned short D_8018AEAA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018AEA8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018AEAA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018AED8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018AED8)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_002/ov_SC04_002_jr_8016AB6C.c b/src/ov_SC04_002/ov_SC04_002_jr_8016AB6C.c index 62e487faae..9d8031862a 100644 --- a/src/ov_SC04_002/ov_SC04_002_jr_8016AB6C.c +++ b/src/ov_SC04_002/ov_SC04_002_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_002/nonmatchings/ov_SC04_002_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018B920[])(void); diff --git a/src/ov_SC04_003/ov_SC04_003_after.c b/src/ov_SC04_003/ov_SC04_003_after.c index 801165b330..58fe25e2c2 100644 --- a/src/ov_SC04_003/ov_SC04_003_after.c +++ b/src/ov_SC04_003/ov_SC04_003_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_003/ov_SC04_003_jr_8012ACE0.c b/src/ov_SC04_003/ov_SC04_003_jr_8012ACE0.c index 4e434daf60..df3e645ec6 100644 --- a/src/ov_SC04_003/ov_SC04_003_jr_8012ACE0.c +++ b/src/ov_SC04_003/ov_SC04_003_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_003/ov_SC04_003_jr_80135D20.c b/src/ov_SC04_003/ov_SC04_003_jr_80135D20.c index 184a9df01d..95c294b531 100644 --- a/src/ov_SC04_003/ov_SC04_003_jr_80135D20.c +++ b/src/ov_SC04_003/ov_SC04_003_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_003/ov_SC04_003_jr_80140608.c b/src/ov_SC04_003/ov_SC04_003_jr_80140608.c index 1b5df6b168..2ba1694700 100644 --- a/src/ov_SC04_003/ov_SC04_003_jr_80140608.c +++ b/src/ov_SC04_003/ov_SC04_003_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183888[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183888[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_003/ov_SC04_003_jr_80154C24.c b/src/ov_SC04_003/ov_SC04_003_jr_80154C24.c index 0d5e200cb7..b62c7fe14f 100644 --- a/src/ov_SC04_003/ov_SC04_003_jr_80154C24.c +++ b/src/ov_SC04_003/ov_SC04_003_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_003/ov_SC04_003_jr_8015C32C.c b/src/ov_SC04_003/ov_SC04_003_jr_8015C32C.c index 6a4a98567e..1569de658d 100644 --- a/src/ov_SC04_003/ov_SC04_003_jr_8015C32C.c +++ b/src/ov_SC04_003/ov_SC04_003_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184C44[]; + extern unsigned short D_80184C46[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184C44[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184C46[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184C74; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184C74)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_003/ov_SC04_003_jr_8016AB6C.c b/src/ov_SC04_003/ov_SC04_003_jr_8016AB6C.c index a2dddc877c..d5e6281963 100644 --- a/src/ov_SC04_003/ov_SC04_003_jr_8016AB6C.c +++ b/src/ov_SC04_003/ov_SC04_003_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_003/nonmatchings/ov_SC04_003_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801856BC[])(void); diff --git a/src/ov_SC04_004/ov_SC04_004_after.c b/src/ov_SC04_004/ov_SC04_004_after.c index 91adc9fe35..c0d84c9748 100644 --- a/src/ov_SC04_004/ov_SC04_004_after.c +++ b/src/ov_SC04_004/ov_SC04_004_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_004/ov_SC04_004_jr_8012ACE0.c b/src/ov_SC04_004/ov_SC04_004_jr_8012ACE0.c index a6e078c1ac..a4998c2afd 100644 --- a/src/ov_SC04_004/ov_SC04_004_jr_8012ACE0.c +++ b/src/ov_SC04_004/ov_SC04_004_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_004/ov_SC04_004_jr_80135D20.c b/src/ov_SC04_004/ov_SC04_004_jr_80135D20.c index 2d214e58e6..7cc6942a7f 100644 --- a/src/ov_SC04_004/ov_SC04_004_jr_80135D20.c +++ b/src/ov_SC04_004/ov_SC04_004_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_004/ov_SC04_004_jr_80140608.c b/src/ov_SC04_004/ov_SC04_004_jr_80140608.c index 6f83cd2d5e..73b60e071f 100644 --- a/src/ov_SC04_004/ov_SC04_004_jr_80140608.c +++ b/src/ov_SC04_004/ov_SC04_004_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80185DBC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80185DBC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_004/ov_SC04_004_jr_80154C24.c b/src/ov_SC04_004/ov_SC04_004_jr_80154C24.c index 6d63116348..6dfb08fa8a 100644 --- a/src/ov_SC04_004/ov_SC04_004_jr_80154C24.c +++ b/src/ov_SC04_004/ov_SC04_004_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_004/ov_SC04_004_jr_8015C32C.c b/src/ov_SC04_004/ov_SC04_004_jr_8015C32C.c index 507808a13e..c27478d0e4 100644 --- a/src/ov_SC04_004/ov_SC04_004_jr_8015C32C.c +++ b/src/ov_SC04_004/ov_SC04_004_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80187178[]; + extern unsigned short D_8018717A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80187178[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018717A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801871A8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801871A8)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_004/ov_SC04_004_jr_8016AB6C.c b/src/ov_SC04_004/ov_SC04_004_jr_8016AB6C.c index 9b5a46e4ca..a3855feae5 100644 --- a/src/ov_SC04_004/ov_SC04_004_jr_8016AB6C.c +++ b/src/ov_SC04_004/ov_SC04_004_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_004/nonmatchings/ov_SC04_004_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80187BF0[])(void); diff --git a/src/ov_SC04_005/ov_SC04_005_after.c b/src/ov_SC04_005/ov_SC04_005_after.c index ff60be3152..7456bf70d2 100644 --- a/src/ov_SC04_005/ov_SC04_005_after.c +++ b/src/ov_SC04_005/ov_SC04_005_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_005/ov_SC04_005_jr_8012ACE0.c b/src/ov_SC04_005/ov_SC04_005_jr_8012ACE0.c index ce6102f105..dcfb29d043 100644 --- a/src/ov_SC04_005/ov_SC04_005_jr_8012ACE0.c +++ b/src/ov_SC04_005/ov_SC04_005_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_005/ov_SC04_005_jr_80135D20.c b/src/ov_SC04_005/ov_SC04_005_jr_80135D20.c index aeecc5ba04..82c5252535 100644 --- a/src/ov_SC04_005/ov_SC04_005_jr_80135D20.c +++ b/src/ov_SC04_005/ov_SC04_005_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_005/ov_SC04_005_jr_80140608.c b/src/ov_SC04_005/ov_SC04_005_jr_80140608.c index cd03f77420..cd64dd2d3d 100644 --- a/src/ov_SC04_005/ov_SC04_005_jr_80140608.c +++ b/src/ov_SC04_005/ov_SC04_005_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801891F0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801891F0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_005/ov_SC04_005_jr_80154C24.c b/src/ov_SC04_005/ov_SC04_005_jr_80154C24.c index 2885f22e10..8d0ac23269 100644 --- a/src/ov_SC04_005/ov_SC04_005_jr_80154C24.c +++ b/src/ov_SC04_005/ov_SC04_005_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_005/ov_SC04_005_jr_8015C32C.c b/src/ov_SC04_005/ov_SC04_005_jr_8015C32C.c index 1335ef8ac7..4c40775dc8 100644 --- a/src/ov_SC04_005/ov_SC04_005_jr_8015C32C.c +++ b/src/ov_SC04_005/ov_SC04_005_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018A5AC[]; + extern unsigned short D_8018A5AE[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018A5AC[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018A5AE[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018A5DC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018A5DC)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_005/ov_SC04_005_jr_8016AB6C.c b/src/ov_SC04_005/ov_SC04_005_jr_8016AB6C.c index 9b62825961..78286fd194 100644 --- a/src/ov_SC04_005/ov_SC04_005_jr_8016AB6C.c +++ b/src/ov_SC04_005/ov_SC04_005_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_005/nonmatchings/ov_SC04_005_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018B024[])(void); diff --git a/src/ov_SC04_006/ov_SC04_006_after.c b/src/ov_SC04_006/ov_SC04_006_after.c index 262c390c2a..e99850d288 100644 --- a/src/ov_SC04_006/ov_SC04_006_after.c +++ b/src/ov_SC04_006/ov_SC04_006_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_006/ov_SC04_006_jr_8012ACE0.c b/src/ov_SC04_006/ov_SC04_006_jr_8012ACE0.c index 26825911d6..789a8e316e 100644 --- a/src/ov_SC04_006/ov_SC04_006_jr_8012ACE0.c +++ b/src/ov_SC04_006/ov_SC04_006_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_006/ov_SC04_006_jr_80135D20.c b/src/ov_SC04_006/ov_SC04_006_jr_80135D20.c index f59aa672f9..a797aae294 100644 --- a/src/ov_SC04_006/ov_SC04_006_jr_80135D20.c +++ b/src/ov_SC04_006/ov_SC04_006_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_006/ov_SC04_006_jr_80140608.c b/src/ov_SC04_006/ov_SC04_006_jr_80140608.c index 09f7608830..0f61180af3 100644 --- a/src/ov_SC04_006/ov_SC04_006_jr_80140608.c +++ b/src/ov_SC04_006/ov_SC04_006_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801812D0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801812D0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_006/ov_SC04_006_jr_80154C24.c b/src/ov_SC04_006/ov_SC04_006_jr_80154C24.c index 91e8c3313c..f197fc982b 100644 --- a/src/ov_SC04_006/ov_SC04_006_jr_80154C24.c +++ b/src/ov_SC04_006/ov_SC04_006_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_006/ov_SC04_006_jr_8015C32C.c b/src/ov_SC04_006/ov_SC04_006_jr_8015C32C.c index 7ba3f46000..89919bdd56 100644 --- a/src/ov_SC04_006/ov_SC04_006_jr_8015C32C.c +++ b/src/ov_SC04_006/ov_SC04_006_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018268C[]; + extern unsigned short D_8018268E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018268C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018268E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801826BC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801826BC)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_006/ov_SC04_006_jr_8016AB6C.c b/src/ov_SC04_006/ov_SC04_006_jr_8016AB6C.c index f3f67576b8..ed75866aba 100644 --- a/src/ov_SC04_006/ov_SC04_006_jr_8016AB6C.c +++ b/src/ov_SC04_006/ov_SC04_006_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_006/nonmatchings/ov_SC04_006_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80183104[])(void); diff --git a/src/ov_SC04_007/ov_SC04_007_after.c b/src/ov_SC04_007/ov_SC04_007_after.c index cd2a16d6f7..29ff5bf4fd 100644 --- a/src/ov_SC04_007/ov_SC04_007_after.c +++ b/src/ov_SC04_007/ov_SC04_007_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_007/ov_SC04_007_jr_8012ACE0.c b/src/ov_SC04_007/ov_SC04_007_jr_8012ACE0.c index 469c3a8408..d847beb000 100644 --- a/src/ov_SC04_007/ov_SC04_007_jr_8012ACE0.c +++ b/src/ov_SC04_007/ov_SC04_007_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_007/ov_SC04_007_jr_80135D20.c b/src/ov_SC04_007/ov_SC04_007_jr_80135D20.c index 2137422f81..3e780a29a1 100644 --- a/src/ov_SC04_007/ov_SC04_007_jr_80135D20.c +++ b/src/ov_SC04_007/ov_SC04_007_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_007/ov_SC04_007_jr_80140608.c b/src/ov_SC04_007/ov_SC04_007_jr_80140608.c index 4a7a8d55d3..6af48f61b2 100644 --- a/src/ov_SC04_007/ov_SC04_007_jr_80140608.c +++ b/src/ov_SC04_007/ov_SC04_007_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80187288[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80187288[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_007/ov_SC04_007_jr_80154C24.c b/src/ov_SC04_007/ov_SC04_007_jr_80154C24.c index 29f560367f..2cd195707e 100644 --- a/src/ov_SC04_007/ov_SC04_007_jr_80154C24.c +++ b/src/ov_SC04_007/ov_SC04_007_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_007/ov_SC04_007_jr_8015C32C.c b/src/ov_SC04_007/ov_SC04_007_jr_8015C32C.c index a631f42f4a..f93930932d 100644 --- a/src/ov_SC04_007/ov_SC04_007_jr_8015C32C.c +++ b/src/ov_SC04_007/ov_SC04_007_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80188644[]; + extern unsigned short D_80188646[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80188644[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80188646[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80188674; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80188674)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_007/ov_SC04_007_jr_8016AB6C.c b/src/ov_SC04_007/ov_SC04_007_jr_8016AB6C.c index 7a7abd2ab4..eee3bb4571 100644 --- a/src/ov_SC04_007/ov_SC04_007_jr_8016AB6C.c +++ b/src/ov_SC04_007/ov_SC04_007_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_007/nonmatchings/ov_SC04_007_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801890BC[])(void); diff --git a/src/ov_SC04_008/ov_SC04_008_after.c b/src/ov_SC04_008/ov_SC04_008_after.c index f476c3cfe8..df1adb03dc 100644 --- a/src/ov_SC04_008/ov_SC04_008_after.c +++ b/src/ov_SC04_008/ov_SC04_008_after.c @@ -2279,7 +2279,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2994,7 +3068,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_008/ov_SC04_008_jr_8012ACE0.c b/src/ov_SC04_008/ov_SC04_008_jr_8012ACE0.c index a8db1c7b07..1de2da095a 100644 --- a/src/ov_SC04_008/ov_SC04_008_jr_8012ACE0.c +++ b/src/ov_SC04_008/ov_SC04_008_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_008/ov_SC04_008_jr_80135D20.c b/src/ov_SC04_008/ov_SC04_008_jr_80135D20.c index 3ba64317b9..ee636e2b4a 100644 --- a/src/ov_SC04_008/ov_SC04_008_jr_80135D20.c +++ b/src/ov_SC04_008/ov_SC04_008_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_008/ov_SC04_008_jr_80140608.c b/src/ov_SC04_008/ov_SC04_008_jr_80140608.c index 4261227399..5792100a6b 100644 --- a/src/ov_SC04_008/ov_SC04_008_jr_80140608.c +++ b/src/ov_SC04_008/ov_SC04_008_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801801C0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801801C0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_008/ov_SC04_008_jr_80154C24.c b/src/ov_SC04_008/ov_SC04_008_jr_80154C24.c index 859f48b02b..d17af7294d 100644 --- a/src/ov_SC04_008/ov_SC04_008_jr_80154C24.c +++ b/src/ov_SC04_008/ov_SC04_008_jr_80154C24.c @@ -1380,7 +1380,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_008/ov_SC04_008_jr_8015C32C.c b/src/ov_SC04_008/ov_SC04_008_jr_8015C32C.c index 82a359ac52..d47ba52efe 100644 --- a/src/ov_SC04_008/ov_SC04_008_jr_8015C32C.c +++ b/src/ov_SC04_008/ov_SC04_008_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018157C[]; + extern unsigned short D_8018157E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018157C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018157E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801815AC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801815AC)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_008/ov_SC04_008_jr_8016AB6C.c b/src/ov_SC04_008/ov_SC04_008_jr_8016AB6C.c index e1644f0b2f..7c58b3b672 100644 --- a/src/ov_SC04_008/ov_SC04_008_jr_8016AB6C.c +++ b/src/ov_SC04_008/ov_SC04_008_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_008/nonmatchings/ov_SC04_008_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80181FF4[])(void); diff --git a/src/ov_SC04_009/ov_SC04_009_after.c b/src/ov_SC04_009/ov_SC04_009_after.c index 44a06a329d..3048dc1fbd 100644 --- a/src/ov_SC04_009/ov_SC04_009_after.c +++ b/src/ov_SC04_009/ov_SC04_009_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_009/ov_SC04_009_jr_8012ACE0.c b/src/ov_SC04_009/ov_SC04_009_jr_8012ACE0.c index a68c606d16..f632a1a537 100644 --- a/src/ov_SC04_009/ov_SC04_009_jr_8012ACE0.c +++ b/src/ov_SC04_009/ov_SC04_009_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_009/ov_SC04_009_jr_80135D20.c b/src/ov_SC04_009/ov_SC04_009_jr_80135D20.c index da4570dd9a..a4d4908088 100644 --- a/src/ov_SC04_009/ov_SC04_009_jr_80135D20.c +++ b/src/ov_SC04_009/ov_SC04_009_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_009/ov_SC04_009_jr_80140608.c b/src/ov_SC04_009/ov_SC04_009_jr_80140608.c index c9d084cad2..6029ecd99b 100644 --- a/src/ov_SC04_009/ov_SC04_009_jr_80140608.c +++ b/src/ov_SC04_009/ov_SC04_009_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801801EC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801801EC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_009/ov_SC04_009_jr_80154C24.c b/src/ov_SC04_009/ov_SC04_009_jr_80154C24.c index ddde00cd8f..716fdf36eb 100644 --- a/src/ov_SC04_009/ov_SC04_009_jr_80154C24.c +++ b/src/ov_SC04_009/ov_SC04_009_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_009/ov_SC04_009_jr_8015C32C.c b/src/ov_SC04_009/ov_SC04_009_jr_8015C32C.c index d048682bab..dbf3e1ff33 100644 --- a/src/ov_SC04_009/ov_SC04_009_jr_8015C32C.c +++ b/src/ov_SC04_009/ov_SC04_009_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801815A8[]; + extern unsigned short D_801815AA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801815A8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801815AA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801815D8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801815D8)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_009/ov_SC04_009_jr_8016AB6C.c b/src/ov_SC04_009/ov_SC04_009_jr_8016AB6C.c index fca0607090..d2bf05cfd1 100644 --- a/src/ov_SC04_009/ov_SC04_009_jr_8016AB6C.c +++ b/src/ov_SC04_009/ov_SC04_009_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_009/nonmatchings/ov_SC04_009_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80182020[])(void); diff --git a/src/ov_SC04_010/ov_SC04_010_after.c b/src/ov_SC04_010/ov_SC04_010_after.c index 49d120e3fb..edb352bbc7 100644 --- a/src/ov_SC04_010/ov_SC04_010_after.c +++ b/src/ov_SC04_010/ov_SC04_010_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_010/ov_SC04_010_jr_8012ACE0.c b/src/ov_SC04_010/ov_SC04_010_jr_8012ACE0.c index 094019bf08..5f299df9a5 100644 --- a/src/ov_SC04_010/ov_SC04_010_jr_8012ACE0.c +++ b/src/ov_SC04_010/ov_SC04_010_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_010/ov_SC04_010_jr_80135D20.c b/src/ov_SC04_010/ov_SC04_010_jr_80135D20.c index c781bbc40c..699738b09e 100644 --- a/src/ov_SC04_010/ov_SC04_010_jr_80135D20.c +++ b/src/ov_SC04_010/ov_SC04_010_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_010/ov_SC04_010_jr_80140608.c b/src/ov_SC04_010/ov_SC04_010_jr_80140608.c index 193000cf75..5bc7306ab7 100644 --- a/src/ov_SC04_010/ov_SC04_010_jr_80140608.c +++ b/src/ov_SC04_010/ov_SC04_010_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017F898[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017F898[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_010/ov_SC04_010_jr_80154C24.c b/src/ov_SC04_010/ov_SC04_010_jr_80154C24.c index 319b2ec1d4..ac7bfe7662 100644 --- a/src/ov_SC04_010/ov_SC04_010_jr_80154C24.c +++ b/src/ov_SC04_010/ov_SC04_010_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_010/ov_SC04_010_jr_8015C32C.c b/src/ov_SC04_010/ov_SC04_010_jr_8015C32C.c index 28d7cc1b0c..5b58ea76ca 100644 --- a/src/ov_SC04_010/ov_SC04_010_jr_8015C32C.c +++ b/src/ov_SC04_010/ov_SC04_010_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80180C54[]; + extern unsigned short D_80180C56[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80180C54[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80180C56[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80180C84; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80180C84)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_010/ov_SC04_010_jr_8016AB6C.c b/src/ov_SC04_010/ov_SC04_010_jr_8016AB6C.c index 64b5184198..8c0c4de07f 100644 --- a/src/ov_SC04_010/ov_SC04_010_jr_8016AB6C.c +++ b/src/ov_SC04_010/ov_SC04_010_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_010/nonmatchings/ov_SC04_010_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801816CC[])(void); diff --git a/src/ov_SC04_011/ov_SC04_011_after.c b/src/ov_SC04_011/ov_SC04_011_after.c index a62e4b4a8f..80a9f4fd57 100644 --- a/src/ov_SC04_011/ov_SC04_011_after.c +++ b/src/ov_SC04_011/ov_SC04_011_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_011/ov_SC04_011_jr_8012ACE0.c b/src/ov_SC04_011/ov_SC04_011_jr_8012ACE0.c index 781806ec8a..5dabdf9882 100644 --- a/src/ov_SC04_011/ov_SC04_011_jr_8012ACE0.c +++ b/src/ov_SC04_011/ov_SC04_011_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_011/ov_SC04_011_jr_80135D20.c b/src/ov_SC04_011/ov_SC04_011_jr_80135D20.c index 90d4d68796..ea91e6d333 100644 --- a/src/ov_SC04_011/ov_SC04_011_jr_80135D20.c +++ b/src/ov_SC04_011/ov_SC04_011_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_011/ov_SC04_011_jr_80140608.c b/src/ov_SC04_011/ov_SC04_011_jr_80140608.c index b9691b2e0c..a49502add1 100644 --- a/src/ov_SC04_011/ov_SC04_011_jr_80140608.c +++ b/src/ov_SC04_011/ov_SC04_011_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80191B28[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80191B28[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_011/ov_SC04_011_jr_80154C24.c b/src/ov_SC04_011/ov_SC04_011_jr_80154C24.c index d1bd661644..540947e868 100644 --- a/src/ov_SC04_011/ov_SC04_011_jr_80154C24.c +++ b/src/ov_SC04_011/ov_SC04_011_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_011/ov_SC04_011_jr_8015C32C.c b/src/ov_SC04_011/ov_SC04_011_jr_8015C32C.c index 821bd03dd0..9af5bb33f1 100644 --- a/src/ov_SC04_011/ov_SC04_011_jr_8015C32C.c +++ b/src/ov_SC04_011/ov_SC04_011_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80192EE4[]; + extern unsigned short D_80192EE6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80192EE4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80192EE6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80192F14; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80192F14)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_011/ov_SC04_011_jr_8016AB6C.c b/src/ov_SC04_011/ov_SC04_011_jr_8016AB6C.c index 399e117705..e66d69a38a 100644 --- a/src/ov_SC04_011/ov_SC04_011_jr_8016AB6C.c +++ b/src/ov_SC04_011/ov_SC04_011_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_011/nonmatchings/ov_SC04_011_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8019395C[])(void); diff --git a/src/ov_SC04_012/ov_SC04_012_after.c b/src/ov_SC04_012/ov_SC04_012_after.c index 77d273cbc1..62c7c8f9f1 100644 --- a/src/ov_SC04_012/ov_SC04_012_after.c +++ b/src/ov_SC04_012/ov_SC04_012_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_012/ov_SC04_012_jr_8012ACE0.c b/src/ov_SC04_012/ov_SC04_012_jr_8012ACE0.c index 8235c7699c..e6c51733fb 100644 --- a/src/ov_SC04_012/ov_SC04_012_jr_8012ACE0.c +++ b/src/ov_SC04_012/ov_SC04_012_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_012/ov_SC04_012_jr_80135D20.c b/src/ov_SC04_012/ov_SC04_012_jr_80135D20.c index afb9fc1a64..995bf5ce75 100644 --- a/src/ov_SC04_012/ov_SC04_012_jr_80135D20.c +++ b/src/ov_SC04_012/ov_SC04_012_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_012/ov_SC04_012_jr_80140608.c b/src/ov_SC04_012/ov_SC04_012_jr_80140608.c index 6cdb886474..e2f72df950 100644 --- a/src/ov_SC04_012/ov_SC04_012_jr_80140608.c +++ b/src/ov_SC04_012/ov_SC04_012_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017FCFC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017FCFC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_012/ov_SC04_012_jr_80154C24.c b/src/ov_SC04_012/ov_SC04_012_jr_80154C24.c index bbadc7bdbe..98a898a9b9 100644 --- a/src/ov_SC04_012/ov_SC04_012_jr_80154C24.c +++ b/src/ov_SC04_012/ov_SC04_012_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_012/ov_SC04_012_jr_8015C32C.c b/src/ov_SC04_012/ov_SC04_012_jr_8015C32C.c index eb04e2bc1e..9549a76a76 100644 --- a/src/ov_SC04_012/ov_SC04_012_jr_8015C32C.c +++ b/src/ov_SC04_012/ov_SC04_012_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801810B8[]; + extern unsigned short D_801810BA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801810B8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801810BA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801810E8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801810E8)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_012/ov_SC04_012_jr_8016AB6C.c b/src/ov_SC04_012/ov_SC04_012_jr_8016AB6C.c index 4a63490dad..838f9a4919 100644 --- a/src/ov_SC04_012/ov_SC04_012_jr_8016AB6C.c +++ b/src/ov_SC04_012/ov_SC04_012_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_012/nonmatchings/ov_SC04_012_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80181B30[])(void); diff --git a/src/ov_SC04_015/ov_SC04_015_after.c b/src/ov_SC04_015/ov_SC04_015_after.c index 4448b39dcc..b34304b826 100644 --- a/src/ov_SC04_015/ov_SC04_015_after.c +++ b/src/ov_SC04_015/ov_SC04_015_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_015/ov_SC04_015_jr_8012ACE0.c b/src/ov_SC04_015/ov_SC04_015_jr_8012ACE0.c index 48610c8e1f..3dc063cb4d 100644 --- a/src/ov_SC04_015/ov_SC04_015_jr_8012ACE0.c +++ b/src/ov_SC04_015/ov_SC04_015_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_015/ov_SC04_015_jr_80135D20.c b/src/ov_SC04_015/ov_SC04_015_jr_80135D20.c index 586d721643..4d0738a439 100644 --- a/src/ov_SC04_015/ov_SC04_015_jr_80135D20.c +++ b/src/ov_SC04_015/ov_SC04_015_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_015/ov_SC04_015_jr_80140608.c b/src/ov_SC04_015/ov_SC04_015_jr_80140608.c index c7495395c6..f466edd051 100644 --- a/src/ov_SC04_015/ov_SC04_015_jr_80140608.c +++ b/src/ov_SC04_015/ov_SC04_015_jr_80140608.c @@ -1825,7 +1825,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80185C34[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80185C34[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_015/ov_SC04_015_jr_80154C24.c b/src/ov_SC04_015/ov_SC04_015_jr_80154C24.c index 9275c6a20b..6e5302ce41 100644 --- a/src/ov_SC04_015/ov_SC04_015_jr_80154C24.c +++ b/src/ov_SC04_015/ov_SC04_015_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_015/ov_SC04_015_jr_8015C32C.c b/src/ov_SC04_015/ov_SC04_015_jr_8015C32C.c index 55cb734124..b9fa34cdd1 100644 --- a/src/ov_SC04_015/ov_SC04_015_jr_8015C32C.c +++ b/src/ov_SC04_015/ov_SC04_015_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80186FF0[]; + extern unsigned short D_80186FF2[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80186FF0[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80186FF2[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80187020; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80187020)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_015/ov_SC04_015_jr_8016AB6C.c b/src/ov_SC04_015/ov_SC04_015_jr_8016AB6C.c index 2a86a23ed2..7a131982f3 100644 --- a/src/ov_SC04_015/ov_SC04_015_jr_8016AB6C.c +++ b/src/ov_SC04_015/ov_SC04_015_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_015/nonmatchings/ov_SC04_015_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80187A68[])(void); diff --git a/src/ov_SC04_016/ov_SC04_016_after.c b/src/ov_SC04_016/ov_SC04_016_after.c index d579de7b9e..fd2a42d1e1 100644 --- a/src/ov_SC04_016/ov_SC04_016_after.c +++ b/src/ov_SC04_016/ov_SC04_016_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_016/ov_SC04_016_jr_8012ACE0.c b/src/ov_SC04_016/ov_SC04_016_jr_8012ACE0.c index 9d0ba1e8f5..99b8c7b3f0 100644 --- a/src/ov_SC04_016/ov_SC04_016_jr_8012ACE0.c +++ b/src/ov_SC04_016/ov_SC04_016_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_016/ov_SC04_016_jr_80135D20.c b/src/ov_SC04_016/ov_SC04_016_jr_80135D20.c index fc6cb6c069..b496ac6704 100644 --- a/src/ov_SC04_016/ov_SC04_016_jr_80135D20.c +++ b/src/ov_SC04_016/ov_SC04_016_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_016/ov_SC04_016_jr_80140608.c b/src/ov_SC04_016/ov_SC04_016_jr_80140608.c index 3247267a96..5b5f14b8fb 100644 --- a/src/ov_SC04_016/ov_SC04_016_jr_80140608.c +++ b/src/ov_SC04_016/ov_SC04_016_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801816F8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801816F8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_016/ov_SC04_016_jr_80154C24.c b/src/ov_SC04_016/ov_SC04_016_jr_80154C24.c index 21784a5b8a..0250686288 100644 --- a/src/ov_SC04_016/ov_SC04_016_jr_80154C24.c +++ b/src/ov_SC04_016/ov_SC04_016_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_016/ov_SC04_016_jr_8015C32C.c b/src/ov_SC04_016/ov_SC04_016_jr_8015C32C.c index d41ccd95a6..65b02e953b 100644 --- a/src/ov_SC04_016/ov_SC04_016_jr_8015C32C.c +++ b/src/ov_SC04_016/ov_SC04_016_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80182AB4[]; + extern unsigned short D_80182AB6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80182AB4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80182AB6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80182AE4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80182AE4)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_016/ov_SC04_016_jr_8016AB6C.c b/src/ov_SC04_016/ov_SC04_016_jr_8016AB6C.c index 91effdc127..c69574fc49 100644 --- a/src/ov_SC04_016/ov_SC04_016_jr_8016AB6C.c +++ b/src/ov_SC04_016/ov_SC04_016_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_016/nonmatchings/ov_SC04_016_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018352C[])(void); diff --git a/src/ov_SC04_018/ov_SC04_018_after.c b/src/ov_SC04_018/ov_SC04_018_after.c index 33ce8cb4a4..cbd08745ce 100644 --- a/src/ov_SC04_018/ov_SC04_018_after.c +++ b/src/ov_SC04_018/ov_SC04_018_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_018/ov_SC04_018_jr_8012ACE0.c b/src/ov_SC04_018/ov_SC04_018_jr_8012ACE0.c index e8252011df..e7f80551bd 100644 --- a/src/ov_SC04_018/ov_SC04_018_jr_8012ACE0.c +++ b/src/ov_SC04_018/ov_SC04_018_jr_8012ACE0.c @@ -990,7 +990,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1900,9 +2035,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_018/ov_SC04_018_jr_80135D20.c b/src/ov_SC04_018/ov_SC04_018_jr_80135D20.c index 1c01391636..ed277d396e 100644 --- a/src/ov_SC04_018/ov_SC04_018_jr_80135D20.c +++ b/src/ov_SC04_018/ov_SC04_018_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_018/ov_SC04_018_jr_80140608.c b/src/ov_SC04_018/ov_SC04_018_jr_80140608.c index a5de054a25..5fb478b1a1 100644 --- a/src/ov_SC04_018/ov_SC04_018_jr_80140608.c +++ b/src/ov_SC04_018/ov_SC04_018_jr_80140608.c @@ -1811,7 +1811,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018E910[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018E910[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_018/ov_SC04_018_jr_80154C24.c b/src/ov_SC04_018/ov_SC04_018_jr_80154C24.c index b2c4f20873..23aafd8d6d 100644 --- a/src/ov_SC04_018/ov_SC04_018_jr_80154C24.c +++ b/src/ov_SC04_018/ov_SC04_018_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_018/ov_SC04_018_jr_8015C32C.c b/src/ov_SC04_018/ov_SC04_018_jr_8015C32C.c index f729515acf..2a8faee94f 100644 --- a/src/ov_SC04_018/ov_SC04_018_jr_8015C32C.c +++ b/src/ov_SC04_018/ov_SC04_018_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018FCCC[]; + extern unsigned short D_8018FCCE[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018FCCC[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018FCCE[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018FCFC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018FCFC)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_018/ov_SC04_018_jr_8016AB6C.c b/src/ov_SC04_018/ov_SC04_018_jr_8016AB6C.c index df6e1625bc..17e661958f 100644 --- a/src/ov_SC04_018/ov_SC04_018_jr_8016AB6C.c +++ b/src/ov_SC04_018/ov_SC04_018_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_018/nonmatchings/ov_SC04_018_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80190744[])(void); diff --git a/src/ov_SC04_019/ov_SC04_019_after.c b/src/ov_SC04_019/ov_SC04_019_after.c index b7e88db3fc..d3bd747f15 100644 --- a/src/ov_SC04_019/ov_SC04_019_after.c +++ b/src/ov_SC04_019/ov_SC04_019_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_019/ov_SC04_019_jr_8012ACE0.c b/src/ov_SC04_019/ov_SC04_019_jr_8012ACE0.c index f4f0555d34..4800ad8181 100644 --- a/src/ov_SC04_019/ov_SC04_019_jr_8012ACE0.c +++ b/src/ov_SC04_019/ov_SC04_019_jr_8012ACE0.c @@ -990,7 +990,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1900,9 +2035,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_019/ov_SC04_019_jr_80135D20.c b/src/ov_SC04_019/ov_SC04_019_jr_80135D20.c index 4606a8b099..8460399eee 100644 --- a/src/ov_SC04_019/ov_SC04_019_jr_80135D20.c +++ b/src/ov_SC04_019/ov_SC04_019_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_019/ov_SC04_019_jr_80140608.c b/src/ov_SC04_019/ov_SC04_019_jr_80140608.c index 8bff314660..f64d55df60 100644 --- a/src/ov_SC04_019/ov_SC04_019_jr_80140608.c +++ b/src/ov_SC04_019/ov_SC04_019_jr_80140608.c @@ -1827,7 +1827,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018E910[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018E910[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_019/ov_SC04_019_jr_80154C24.c b/src/ov_SC04_019/ov_SC04_019_jr_80154C24.c index 2ab7c16f42..cafd0c29da 100644 --- a/src/ov_SC04_019/ov_SC04_019_jr_80154C24.c +++ b/src/ov_SC04_019/ov_SC04_019_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_019/ov_SC04_019_jr_8015C32C.c b/src/ov_SC04_019/ov_SC04_019_jr_8015C32C.c index 835f3588d2..90f194eb95 100644 --- a/src/ov_SC04_019/ov_SC04_019_jr_8015C32C.c +++ b/src/ov_SC04_019/ov_SC04_019_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018FCCC[]; + extern unsigned short D_8018FCCE[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018FCCC[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018FCCE[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018FCFC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018FCFC)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_019/ov_SC04_019_jr_8016AB6C.c b/src/ov_SC04_019/ov_SC04_019_jr_8016AB6C.c index 3f64c458da..e296af59b1 100644 --- a/src/ov_SC04_019/ov_SC04_019_jr_8016AB6C.c +++ b/src/ov_SC04_019/ov_SC04_019_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_019/nonmatchings/ov_SC04_019_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80190744[])(void); diff --git a/src/ov_SC04_020/ov_SC04_020_after.c b/src/ov_SC04_020/ov_SC04_020_after.c index 8dd5478606..ad9febe388 100644 --- a/src/ov_SC04_020/ov_SC04_020_after.c +++ b/src/ov_SC04_020/ov_SC04_020_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_020/ov_SC04_020_jr_8012ACE0.c b/src/ov_SC04_020/ov_SC04_020_jr_8012ACE0.c index 3620948696..0f2b187fbe 100644 --- a/src/ov_SC04_020/ov_SC04_020_jr_8012ACE0.c +++ b/src/ov_SC04_020/ov_SC04_020_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_020/ov_SC04_020_jr_80135D20.c b/src/ov_SC04_020/ov_SC04_020_jr_80135D20.c index dd4d8829f0..4d1b1a7e18 100644 --- a/src/ov_SC04_020/ov_SC04_020_jr_80135D20.c +++ b/src/ov_SC04_020/ov_SC04_020_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_020/ov_SC04_020_jr_80140608.c b/src/ov_SC04_020/ov_SC04_020_jr_80140608.c index 46db736ef4..9178f8d7b2 100644 --- a/src/ov_SC04_020/ov_SC04_020_jr_80140608.c +++ b/src/ov_SC04_020/ov_SC04_020_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80184A48[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80184A48[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_020/ov_SC04_020_jr_80154C24.c b/src/ov_SC04_020/ov_SC04_020_jr_80154C24.c index c3f9ff849d..a5a31c5e91 100644 --- a/src/ov_SC04_020/ov_SC04_020_jr_80154C24.c +++ b/src/ov_SC04_020/ov_SC04_020_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_020/ov_SC04_020_jr_8015C32C.c b/src/ov_SC04_020/ov_SC04_020_jr_8015C32C.c index 644cc12c8f..daaf3b3309 100644 --- a/src/ov_SC04_020/ov_SC04_020_jr_8015C32C.c +++ b/src/ov_SC04_020/ov_SC04_020_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80185E04[]; + extern unsigned short D_80185E06[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80185E04[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80185E06[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80185E34; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80185E34)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_020/ov_SC04_020_jr_8016AB6C.c b/src/ov_SC04_020/ov_SC04_020_jr_8016AB6C.c index 5118cd8302..9565dfa8fa 100644 --- a/src/ov_SC04_020/ov_SC04_020_jr_8016AB6C.c +++ b/src/ov_SC04_020/ov_SC04_020_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_020/nonmatchings/ov_SC04_020_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018687C[])(void); diff --git a/src/ov_SC04_021/ov_SC04_021_after.c b/src/ov_SC04_021/ov_SC04_021_after.c index 158e63e8d6..75ad610cfe 100644 --- a/src/ov_SC04_021/ov_SC04_021_after.c +++ b/src/ov_SC04_021/ov_SC04_021_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC04_021/ov_SC04_021_jr_8012ACE0.c b/src/ov_SC04_021/ov_SC04_021_jr_8012ACE0.c index 2b653b4aa5..6add4197a4 100644 --- a/src/ov_SC04_021/ov_SC04_021_jr_8012ACE0.c +++ b/src/ov_SC04_021/ov_SC04_021_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC04_021/ov_SC04_021_jr_80135D20.c b/src/ov_SC04_021/ov_SC04_021_jr_80135D20.c index 9f25a8385e..6bf97a248d 100644 --- a/src/ov_SC04_021/ov_SC04_021_jr_80135D20.c +++ b/src/ov_SC04_021/ov_SC04_021_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC04_021/ov_SC04_021_jr_80140608.c b/src/ov_SC04_021/ov_SC04_021_jr_80140608.c index 4ef28aaaf4..2c914f398e 100644 --- a/src/ov_SC04_021/ov_SC04_021_jr_80140608.c +++ b/src/ov_SC04_021/ov_SC04_021_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80180974[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80180974[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC04_021/ov_SC04_021_jr_80154C24.c b/src/ov_SC04_021/ov_SC04_021_jr_80154C24.c index 06264c4b3c..10412ea9d3 100644 --- a/src/ov_SC04_021/ov_SC04_021_jr_80154C24.c +++ b/src/ov_SC04_021/ov_SC04_021_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC04_021/ov_SC04_021_jr_8015C32C.c b/src/ov_SC04_021/ov_SC04_021_jr_8015C32C.c index f7454f4f30..02818e0b26 100644 --- a/src/ov_SC04_021/ov_SC04_021_jr_8015C32C.c +++ b/src/ov_SC04_021/ov_SC04_021_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80181D30[]; + extern unsigned short D_80181D32[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80181D30[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80181D32[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80181D60; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80181D60)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC04_021/ov_SC04_021_jr_8016AB6C.c b/src/ov_SC04_021/ov_SC04_021_jr_8016AB6C.c index 05e7b70644..2d2c0b8131 100644 --- a/src/ov_SC04_021/ov_SC04_021_jr_8016AB6C.c +++ b/src/ov_SC04_021/ov_SC04_021_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC04_021/nonmatchings/ov_SC04_021_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801827A8[])(void); diff --git a/src/ov_SC05_000/ov_SC05_000_after.c b/src/ov_SC05_000/ov_SC05_000_after.c index 38e54c87e3..06cf4c3c2f 100644 --- a/src/ov_SC05_000/ov_SC05_000_after.c +++ b/src/ov_SC05_000/ov_SC05_000_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_000/ov_SC05_000_jr_8012ACE0.c b/src/ov_SC05_000/ov_SC05_000_jr_8012ACE0.c index c3c9620295..ee15eb8d7b 100644 --- a/src/ov_SC05_000/ov_SC05_000_jr_8012ACE0.c +++ b/src/ov_SC05_000/ov_SC05_000_jr_8012ACE0.c @@ -990,7 +990,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1900,9 +2035,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_000/ov_SC05_000_jr_80135D20.c b/src/ov_SC05_000/ov_SC05_000_jr_80135D20.c index 58733b139f..ad3eee7624 100644 --- a/src/ov_SC05_000/ov_SC05_000_jr_80135D20.c +++ b/src/ov_SC05_000/ov_SC05_000_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_000/ov_SC05_000_jr_80140608.c b/src/ov_SC05_000/ov_SC05_000_jr_80140608.c index 60a300057e..e78aae290f 100644 --- a/src/ov_SC05_000/ov_SC05_000_jr_80140608.c +++ b/src/ov_SC05_000/ov_SC05_000_jr_80140608.c @@ -1811,7 +1811,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017F988[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017F988[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_000/ov_SC05_000_jr_80154C24.c b/src/ov_SC05_000/ov_SC05_000_jr_80154C24.c index af8e310abc..759d2274f3 100644 --- a/src/ov_SC05_000/ov_SC05_000_jr_80154C24.c +++ b/src/ov_SC05_000/ov_SC05_000_jr_80154C24.c @@ -1356,7 +1356,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_000/ov_SC05_000_jr_8015C32C.c b/src/ov_SC05_000/ov_SC05_000_jr_8015C32C.c index fd268b1db6..2f8c669476 100644 --- a/src/ov_SC05_000/ov_SC05_000_jr_8015C32C.c +++ b/src/ov_SC05_000/ov_SC05_000_jr_8015C32C.c @@ -3286,7 +3286,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3572,7 +3623,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80180D44[]; + extern unsigned short D_80180D46[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80180D44[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80180D46[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3590,7 +3664,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80180D74; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80180D74)[param_2]); + } + } +} + // @class: struct @@ -5925,7 +6050,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_000/ov_SC05_000_jr_8016AB6C.c b/src/ov_SC05_000/ov_SC05_000_jr_8016AB6C.c index 8171e64ce1..6fe8e39aee 100644 --- a/src/ov_SC05_000/ov_SC05_000_jr_8016AB6C.c +++ b/src/ov_SC05_000/ov_SC05_000_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3880,7 +3878,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4243,7 +4297,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4293,7 +4368,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_000/nonmatchings/ov_SC05_000_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801817BC[])(void); diff --git a/src/ov_SC05_001/ov_SC05_001_after.c b/src/ov_SC05_001/ov_SC05_001_after.c index ebfd988261..315ae004ea 100644 --- a/src/ov_SC05_001/ov_SC05_001_after.c +++ b/src/ov_SC05_001/ov_SC05_001_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_001/ov_SC05_001_jr_8012ACE0.c b/src/ov_SC05_001/ov_SC05_001_jr_8012ACE0.c index c755c6c614..aa78b0ce6c 100644 --- a/src/ov_SC05_001/ov_SC05_001_jr_8012ACE0.c +++ b/src/ov_SC05_001/ov_SC05_001_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_001/ov_SC05_001_jr_80135D20.c b/src/ov_SC05_001/ov_SC05_001_jr_80135D20.c index 9c782003dc..5fa57de9c5 100644 --- a/src/ov_SC05_001/ov_SC05_001_jr_80135D20.c +++ b/src/ov_SC05_001/ov_SC05_001_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_001/ov_SC05_001_jr_80140608.c b/src/ov_SC05_001/ov_SC05_001_jr_80140608.c index f1a89a90dd..70db998fa6 100644 --- a/src/ov_SC05_001/ov_SC05_001_jr_80140608.c +++ b/src/ov_SC05_001/ov_SC05_001_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80187164[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80187164[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_001/ov_SC05_001_jr_80154C24.c b/src/ov_SC05_001/ov_SC05_001_jr_80154C24.c index 2f607b4264..23768c7b2d 100644 --- a/src/ov_SC05_001/ov_SC05_001_jr_80154C24.c +++ b/src/ov_SC05_001/ov_SC05_001_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_001/ov_SC05_001_jr_8015C32C.c b/src/ov_SC05_001/ov_SC05_001_jr_8015C32C.c index 6dfc38658a..c102265475 100644 --- a/src/ov_SC05_001/ov_SC05_001_jr_8015C32C.c +++ b/src/ov_SC05_001/ov_SC05_001_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80188520[]; + extern unsigned short D_80188522[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80188520[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80188522[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80188550; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80188550)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_001/ov_SC05_001_jr_8016AB6C.c b/src/ov_SC05_001/ov_SC05_001_jr_8016AB6C.c index d7f4c0ac33..c8c86b3323 100644 --- a/src/ov_SC05_001/ov_SC05_001_jr_8016AB6C.c +++ b/src/ov_SC05_001/ov_SC05_001_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_001/nonmatchings/ov_SC05_001_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80188F98[])(void); diff --git a/src/ov_SC05_002/ov_SC05_002_after.c b/src/ov_SC05_002/ov_SC05_002_after.c index c738c541e2..f455bcc7b4 100644 --- a/src/ov_SC05_002/ov_SC05_002_after.c +++ b/src/ov_SC05_002/ov_SC05_002_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_002/ov_SC05_002_jr_8012ACE0.c b/src/ov_SC05_002/ov_SC05_002_jr_8012ACE0.c index f40814c1fb..0ad789eabb 100644 --- a/src/ov_SC05_002/ov_SC05_002_jr_8012ACE0.c +++ b/src/ov_SC05_002/ov_SC05_002_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_002/ov_SC05_002_jr_80135D20.c b/src/ov_SC05_002/ov_SC05_002_jr_80135D20.c index c3e4fd9914..57981f51df 100644 --- a/src/ov_SC05_002/ov_SC05_002_jr_80135D20.c +++ b/src/ov_SC05_002/ov_SC05_002_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_002/ov_SC05_002_jr_80140608.c b/src/ov_SC05_002/ov_SC05_002_jr_80140608.c index cb2feb1369..a30db7911e 100644 --- a/src/ov_SC05_002/ov_SC05_002_jr_80140608.c +++ b/src/ov_SC05_002/ov_SC05_002_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80181924[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80181924[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_002/ov_SC05_002_jr_80154C24.c b/src/ov_SC05_002/ov_SC05_002_jr_80154C24.c index f45ed6b809..ff37cbee8c 100644 --- a/src/ov_SC05_002/ov_SC05_002_jr_80154C24.c +++ b/src/ov_SC05_002/ov_SC05_002_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_002/ov_SC05_002_jr_8015C32C.c b/src/ov_SC05_002/ov_SC05_002_jr_8015C32C.c index 4e6f750836..d3e33523fc 100644 --- a/src/ov_SC05_002/ov_SC05_002_jr_8015C32C.c +++ b/src/ov_SC05_002/ov_SC05_002_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80182CE0[]; + extern unsigned short D_80182CE2[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80182CE0[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80182CE2[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80182D10; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80182D10)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_002/ov_SC05_002_jr_8016AB6C.c b/src/ov_SC05_002/ov_SC05_002_jr_8016AB6C.c index 0aeafc65e1..e10ea17859 100644 --- a/src/ov_SC05_002/ov_SC05_002_jr_8016AB6C.c +++ b/src/ov_SC05_002/ov_SC05_002_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_002/nonmatchings/ov_SC05_002_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80183758[])(void); diff --git a/src/ov_SC05_003/ov_SC05_003_after.c b/src/ov_SC05_003/ov_SC05_003_after.c index 9378aa1e24..e39bc311f8 100644 --- a/src/ov_SC05_003/ov_SC05_003_after.c +++ b/src/ov_SC05_003/ov_SC05_003_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_003/ov_SC05_003_jr_8012ACE0.c b/src/ov_SC05_003/ov_SC05_003_jr_8012ACE0.c index 2221e8120f..251cc17e00 100644 --- a/src/ov_SC05_003/ov_SC05_003_jr_8012ACE0.c +++ b/src/ov_SC05_003/ov_SC05_003_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_003/ov_SC05_003_jr_80135D20.c b/src/ov_SC05_003/ov_SC05_003_jr_80135D20.c index f63541b147..4827a2bf39 100644 --- a/src/ov_SC05_003/ov_SC05_003_jr_80135D20.c +++ b/src/ov_SC05_003/ov_SC05_003_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_003/ov_SC05_003_jr_80140608.c b/src/ov_SC05_003/ov_SC05_003_jr_80140608.c index a85a3444c9..4522af26bc 100644 --- a/src/ov_SC05_003/ov_SC05_003_jr_80140608.c +++ b/src/ov_SC05_003/ov_SC05_003_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183C5C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183C5C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_003/ov_SC05_003_jr_80154C24.c b/src/ov_SC05_003/ov_SC05_003_jr_80154C24.c index 0fa842db95..2f57490d59 100644 --- a/src/ov_SC05_003/ov_SC05_003_jr_80154C24.c +++ b/src/ov_SC05_003/ov_SC05_003_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_003/ov_SC05_003_jr_8015C32C.c b/src/ov_SC05_003/ov_SC05_003_jr_8015C32C.c index aeed671681..4043dca5cb 100644 --- a/src/ov_SC05_003/ov_SC05_003_jr_8015C32C.c +++ b/src/ov_SC05_003/ov_SC05_003_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80185018[]; + extern unsigned short D_8018501A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80185018[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018501A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80185048; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80185048)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_003/ov_SC05_003_jr_8016AB6C.c b/src/ov_SC05_003/ov_SC05_003_jr_8016AB6C.c index b5bc2baa5f..d99ea0fda7 100644 --- a/src/ov_SC05_003/ov_SC05_003_jr_8016AB6C.c +++ b/src/ov_SC05_003/ov_SC05_003_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_003/nonmatchings/ov_SC05_003_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80185A90[])(void); diff --git a/src/ov_SC05_004/ov_SC05_004_after.c b/src/ov_SC05_004/ov_SC05_004_after.c index b648f176a3..2f56dd2eed 100644 --- a/src/ov_SC05_004/ov_SC05_004_after.c +++ b/src/ov_SC05_004/ov_SC05_004_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_004/ov_SC05_004_jr_8012ACE0.c b/src/ov_SC05_004/ov_SC05_004_jr_8012ACE0.c index 6b7930448d..34de4859aa 100644 --- a/src/ov_SC05_004/ov_SC05_004_jr_8012ACE0.c +++ b/src/ov_SC05_004/ov_SC05_004_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_004/ov_SC05_004_jr_80135D20.c b/src/ov_SC05_004/ov_SC05_004_jr_80135D20.c index 7f72d8ab07..bbc88dd2ab 100644 --- a/src/ov_SC05_004/ov_SC05_004_jr_80135D20.c +++ b/src/ov_SC05_004/ov_SC05_004_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_004/ov_SC05_004_jr_80140608.c b/src/ov_SC05_004/ov_SC05_004_jr_80140608.c index 37a7bce93e..815955109f 100644 --- a/src/ov_SC05_004/ov_SC05_004_jr_80140608.c +++ b/src/ov_SC05_004/ov_SC05_004_jr_80140608.c @@ -1843,7 +1843,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80182D68[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80182D68[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_004/ov_SC05_004_jr_80154C24.c b/src/ov_SC05_004/ov_SC05_004_jr_80154C24.c index c9d2496cb0..1a1b1ac160 100644 --- a/src/ov_SC05_004/ov_SC05_004_jr_80154C24.c +++ b/src/ov_SC05_004/ov_SC05_004_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_004/ov_SC05_004_jr_8015C32C.c b/src/ov_SC05_004/ov_SC05_004_jr_8015C32C.c index 40c8053021..0e76b744cc 100644 --- a/src/ov_SC05_004/ov_SC05_004_jr_8015C32C.c +++ b/src/ov_SC05_004/ov_SC05_004_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184124[]; + extern unsigned short D_80184126[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184124[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184126[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184154; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184154)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_004/ov_SC05_004_jr_8016AB6C.c b/src/ov_SC05_004/ov_SC05_004_jr_8016AB6C.c index cf40206357..25e8073960 100644 --- a/src/ov_SC05_004/ov_SC05_004_jr_8016AB6C.c +++ b/src/ov_SC05_004/ov_SC05_004_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_004/nonmatchings/ov_SC05_004_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80184B9C[])(void); diff --git a/src/ov_SC05_005/ov_SC05_005_after.c b/src/ov_SC05_005/ov_SC05_005_after.c index a0e73d2bf3..5d9aa79cdc 100644 --- a/src/ov_SC05_005/ov_SC05_005_after.c +++ b/src/ov_SC05_005/ov_SC05_005_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_005/ov_SC05_005_jr_8012ACE0.c b/src/ov_SC05_005/ov_SC05_005_jr_8012ACE0.c index b11d317efa..e15bb5adab 100644 --- a/src/ov_SC05_005/ov_SC05_005_jr_8012ACE0.c +++ b/src/ov_SC05_005/ov_SC05_005_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_005/ov_SC05_005_jr_80135D20.c b/src/ov_SC05_005/ov_SC05_005_jr_80135D20.c index 55d4992ba7..1b5c853654 100644 --- a/src/ov_SC05_005/ov_SC05_005_jr_80135D20.c +++ b/src/ov_SC05_005/ov_SC05_005_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_005/ov_SC05_005_jr_80140608.c b/src/ov_SC05_005/ov_SC05_005_jr_80140608.c index 44ff548d44..5d9da3302e 100644 --- a/src/ov_SC05_005/ov_SC05_005_jr_80140608.c +++ b/src/ov_SC05_005/ov_SC05_005_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183B3C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183B3C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_005/ov_SC05_005_jr_80154C24.c b/src/ov_SC05_005/ov_SC05_005_jr_80154C24.c index 5124298dc2..edfdd77e91 100644 --- a/src/ov_SC05_005/ov_SC05_005_jr_80154C24.c +++ b/src/ov_SC05_005/ov_SC05_005_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_005/ov_SC05_005_jr_8015C32C.c b/src/ov_SC05_005/ov_SC05_005_jr_8015C32C.c index 048d738de5..62b4062cfa 100644 --- a/src/ov_SC05_005/ov_SC05_005_jr_8015C32C.c +++ b/src/ov_SC05_005/ov_SC05_005_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184EF8[]; + extern unsigned short D_80184EFA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184EF8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184EFA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184F28; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184F28)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_005/ov_SC05_005_jr_8016AB6C.c b/src/ov_SC05_005/ov_SC05_005_jr_8016AB6C.c index 6a7f458019..7dbe287135 100644 --- a/src/ov_SC05_005/ov_SC05_005_jr_8016AB6C.c +++ b/src/ov_SC05_005/ov_SC05_005_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_005/nonmatchings/ov_SC05_005_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80185970[])(void); diff --git a/src/ov_SC05_006/ov_SC05_006_after.c b/src/ov_SC05_006/ov_SC05_006_after.c index d11807ff39..bb82220e2b 100644 --- a/src/ov_SC05_006/ov_SC05_006_after.c +++ b/src/ov_SC05_006/ov_SC05_006_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_006/ov_SC05_006_jr_8012ACE0.c b/src/ov_SC05_006/ov_SC05_006_jr_8012ACE0.c index e8fb7b90b0..8b067d8198 100644 --- a/src/ov_SC05_006/ov_SC05_006_jr_8012ACE0.c +++ b/src/ov_SC05_006/ov_SC05_006_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_006/ov_SC05_006_jr_80135D20.c b/src/ov_SC05_006/ov_SC05_006_jr_80135D20.c index 88d05923ea..f87ea5667f 100644 --- a/src/ov_SC05_006/ov_SC05_006_jr_80135D20.c +++ b/src/ov_SC05_006/ov_SC05_006_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_006/ov_SC05_006_jr_80140608.c b/src/ov_SC05_006/ov_SC05_006_jr_80140608.c index 691a6a1d4f..5dbab6e6b0 100644 --- a/src/ov_SC05_006/ov_SC05_006_jr_80140608.c +++ b/src/ov_SC05_006/ov_SC05_006_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80180B5C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80180B5C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_006/ov_SC05_006_jr_80154C24.c b/src/ov_SC05_006/ov_SC05_006_jr_80154C24.c index f1c0e3b47f..c14f6f0478 100644 --- a/src/ov_SC05_006/ov_SC05_006_jr_80154C24.c +++ b/src/ov_SC05_006/ov_SC05_006_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_006/ov_SC05_006_jr_8015C32C.c b/src/ov_SC05_006/ov_SC05_006_jr_8015C32C.c index f816a081eb..5a10dfd9c4 100644 --- a/src/ov_SC05_006/ov_SC05_006_jr_8015C32C.c +++ b/src/ov_SC05_006/ov_SC05_006_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80181F18[]; + extern unsigned short D_80181F1A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80181F18[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80181F1A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80181F48; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80181F48)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_006/ov_SC05_006_jr_8016AB6C.c b/src/ov_SC05_006/ov_SC05_006_jr_8016AB6C.c index 50913c9b18..81bbcae1b2 100644 --- a/src/ov_SC05_006/ov_SC05_006_jr_8016AB6C.c +++ b/src/ov_SC05_006/ov_SC05_006_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_006/nonmatchings/ov_SC05_006_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80182990[])(void); diff --git a/src/ov_SC05_007/ov_SC05_007_after.c b/src/ov_SC05_007/ov_SC05_007_after.c index be2c6d3e48..f3e878b650 100644 --- a/src/ov_SC05_007/ov_SC05_007_after.c +++ b/src/ov_SC05_007/ov_SC05_007_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_007/ov_SC05_007_jr_8012ACE0.c b/src/ov_SC05_007/ov_SC05_007_jr_8012ACE0.c index fd2d43a10a..896afc7d13 100644 --- a/src/ov_SC05_007/ov_SC05_007_jr_8012ACE0.c +++ b/src/ov_SC05_007/ov_SC05_007_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_007/ov_SC05_007_jr_80135D20.c b/src/ov_SC05_007/ov_SC05_007_jr_80135D20.c index 3e7f83af07..9942eafee0 100644 --- a/src/ov_SC05_007/ov_SC05_007_jr_80135D20.c +++ b/src/ov_SC05_007/ov_SC05_007_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_007/ov_SC05_007_jr_80140608.c b/src/ov_SC05_007/ov_SC05_007_jr_80140608.c index 6b6c722901..afdeff6e62 100644 --- a/src/ov_SC05_007/ov_SC05_007_jr_80140608.c +++ b/src/ov_SC05_007/ov_SC05_007_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80182158[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80182158[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_007/ov_SC05_007_jr_80154C24.c b/src/ov_SC05_007/ov_SC05_007_jr_80154C24.c index 33d7f55c79..76a50c43dc 100644 --- a/src/ov_SC05_007/ov_SC05_007_jr_80154C24.c +++ b/src/ov_SC05_007/ov_SC05_007_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_007/ov_SC05_007_jr_8015C32C.c b/src/ov_SC05_007/ov_SC05_007_jr_8015C32C.c index e5dde72be2..12d31f375e 100644 --- a/src/ov_SC05_007/ov_SC05_007_jr_8015C32C.c +++ b/src/ov_SC05_007/ov_SC05_007_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80183514[]; + extern unsigned short D_80183516[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80183514[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80183516[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80183544; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80183544)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_007/ov_SC05_007_jr_8016AB6C.c b/src/ov_SC05_007/ov_SC05_007_jr_8016AB6C.c index 88e4b01012..22eab62493 100644 --- a/src/ov_SC05_007/ov_SC05_007_jr_8016AB6C.c +++ b/src/ov_SC05_007/ov_SC05_007_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_007/nonmatchings/ov_SC05_007_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80183F8C[])(void); diff --git a/src/ov_SC05_008/ov_SC05_008_after.c b/src/ov_SC05_008/ov_SC05_008_after.c index 86eb2f10ec..dd43a22477 100644 --- a/src/ov_SC05_008/ov_SC05_008_after.c +++ b/src/ov_SC05_008/ov_SC05_008_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_008/ov_SC05_008_jr_8012ACE0.c b/src/ov_SC05_008/ov_SC05_008_jr_8012ACE0.c index cfa36f2ca4..cba73b2354 100644 --- a/src/ov_SC05_008/ov_SC05_008_jr_8012ACE0.c +++ b/src/ov_SC05_008/ov_SC05_008_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_008/ov_SC05_008_jr_80135D20.c b/src/ov_SC05_008/ov_SC05_008_jr_80135D20.c index 4373e7eb42..7123306cd7 100644 --- a/src/ov_SC05_008/ov_SC05_008_jr_80135D20.c +++ b/src/ov_SC05_008/ov_SC05_008_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_008/ov_SC05_008_jr_80140608.c b/src/ov_SC05_008/ov_SC05_008_jr_80140608.c index 55ed05f925..15b65d0040 100644 --- a/src/ov_SC05_008/ov_SC05_008_jr_80140608.c +++ b/src/ov_SC05_008/ov_SC05_008_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801840B0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801840B0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_008/ov_SC05_008_jr_80154C24.c b/src/ov_SC05_008/ov_SC05_008_jr_80154C24.c index f8d74447ad..5efcc39ee3 100644 --- a/src/ov_SC05_008/ov_SC05_008_jr_80154C24.c +++ b/src/ov_SC05_008/ov_SC05_008_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_008/ov_SC05_008_jr_8015C32C.c b/src/ov_SC05_008/ov_SC05_008_jr_8015C32C.c index dde5256c17..c7df1622cd 100644 --- a/src/ov_SC05_008/ov_SC05_008_jr_8015C32C.c +++ b/src/ov_SC05_008/ov_SC05_008_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018546C[]; + extern unsigned short D_8018546E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018546C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018546E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018549C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018549C)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_008/ov_SC05_008_jr_8016AB6C.c b/src/ov_SC05_008/ov_SC05_008_jr_8016AB6C.c index b41bc80a41..b15a5831fd 100644 --- a/src/ov_SC05_008/ov_SC05_008_jr_8016AB6C.c +++ b/src/ov_SC05_008/ov_SC05_008_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_008/nonmatchings/ov_SC05_008_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80185EE4[])(void); diff --git a/src/ov_SC05_009/ov_SC05_009_after.c b/src/ov_SC05_009/ov_SC05_009_after.c index b2d5a1831c..2a8b6452b8 100644 --- a/src/ov_SC05_009/ov_SC05_009_after.c +++ b/src/ov_SC05_009/ov_SC05_009_after.c @@ -2279,7 +2279,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2994,7 +3068,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_009/ov_SC05_009_jr_8012ACE0.c b/src/ov_SC05_009/ov_SC05_009_jr_8012ACE0.c index f521a4b3ec..2d492f6ed9 100644 --- a/src/ov_SC05_009/ov_SC05_009_jr_8012ACE0.c +++ b/src/ov_SC05_009/ov_SC05_009_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_009/ov_SC05_009_jr_80135D20.c b/src/ov_SC05_009/ov_SC05_009_jr_80135D20.c index 98e7ac5b93..70152cc5d9 100644 --- a/src/ov_SC05_009/ov_SC05_009_jr_80135D20.c +++ b/src/ov_SC05_009/ov_SC05_009_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_009/ov_SC05_009_jr_80140608.c b/src/ov_SC05_009/ov_SC05_009_jr_80140608.c index 86e015476f..73373be71f 100644 --- a/src/ov_SC05_009/ov_SC05_009_jr_80140608.c +++ b/src/ov_SC05_009/ov_SC05_009_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801801C0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801801C0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_009/ov_SC05_009_jr_80154C24.c b/src/ov_SC05_009/ov_SC05_009_jr_80154C24.c index 4a649769cc..a63bce1b30 100644 --- a/src/ov_SC05_009/ov_SC05_009_jr_80154C24.c +++ b/src/ov_SC05_009/ov_SC05_009_jr_80154C24.c @@ -1380,7 +1380,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_009/ov_SC05_009_jr_8015C32C.c b/src/ov_SC05_009/ov_SC05_009_jr_8015C32C.c index aa5b88de3e..4ec7234e29 100644 --- a/src/ov_SC05_009/ov_SC05_009_jr_8015C32C.c +++ b/src/ov_SC05_009/ov_SC05_009_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018157C[]; + extern unsigned short D_8018157E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018157C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018157E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801815AC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801815AC)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_009/ov_SC05_009_jr_8016AB6C.c b/src/ov_SC05_009/ov_SC05_009_jr_8016AB6C.c index 4c37e6a3ed..95beb9c257 100644 --- a/src/ov_SC05_009/ov_SC05_009_jr_8016AB6C.c +++ b/src/ov_SC05_009/ov_SC05_009_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_009/nonmatchings/ov_SC05_009_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80181FF4[])(void); diff --git a/src/ov_SC05_010/ov_SC05_010_after.c b/src/ov_SC05_010/ov_SC05_010_after.c index 49309a6fd0..caaac82721 100644 --- a/src/ov_SC05_010/ov_SC05_010_after.c +++ b/src/ov_SC05_010/ov_SC05_010_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_010/ov_SC05_010_jr_8012ACE0.c b/src/ov_SC05_010/ov_SC05_010_jr_8012ACE0.c index 6627f48214..0483ffccc1 100644 --- a/src/ov_SC05_010/ov_SC05_010_jr_8012ACE0.c +++ b/src/ov_SC05_010/ov_SC05_010_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_010/ov_SC05_010_jr_80135D20.c b/src/ov_SC05_010/ov_SC05_010_jr_80135D20.c index faf2b3d6eb..7c952e1bff 100644 --- a/src/ov_SC05_010/ov_SC05_010_jr_80135D20.c +++ b/src/ov_SC05_010/ov_SC05_010_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_010/ov_SC05_010_jr_80140608.c b/src/ov_SC05_010/ov_SC05_010_jr_80140608.c index dd3432f6fe..42e7b12fde 100644 --- a/src/ov_SC05_010/ov_SC05_010_jr_80140608.c +++ b/src/ov_SC05_010/ov_SC05_010_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018A09C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018A09C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_010/ov_SC05_010_jr_80154C24.c b/src/ov_SC05_010/ov_SC05_010_jr_80154C24.c index b001024bca..54aeb85649 100644 --- a/src/ov_SC05_010/ov_SC05_010_jr_80154C24.c +++ b/src/ov_SC05_010/ov_SC05_010_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_010/ov_SC05_010_jr_8015C32C.c b/src/ov_SC05_010/ov_SC05_010_jr_8015C32C.c index c387e8ab9f..3a3c81ffa2 100644 --- a/src/ov_SC05_010/ov_SC05_010_jr_8015C32C.c +++ b/src/ov_SC05_010/ov_SC05_010_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018B458[]; + extern unsigned short D_8018B45A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018B458[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018B45A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018B488; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018B488)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_010/ov_SC05_010_jr_8016AB6C.c b/src/ov_SC05_010/ov_SC05_010_jr_8016AB6C.c index 7f5f9f8c7b..0778bfe0c4 100644 --- a/src/ov_SC05_010/ov_SC05_010_jr_8016AB6C.c +++ b/src/ov_SC05_010/ov_SC05_010_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_010/nonmatchings/ov_SC05_010_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018BED0[])(void); diff --git a/src/ov_SC05_011/ov_SC05_011_after.c b/src/ov_SC05_011/ov_SC05_011_after.c index a5e3dc4609..6741210aa2 100644 --- a/src/ov_SC05_011/ov_SC05_011_after.c +++ b/src/ov_SC05_011/ov_SC05_011_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_011/ov_SC05_011_jr_8012ACE0.c b/src/ov_SC05_011/ov_SC05_011_jr_8012ACE0.c index 017621bf2d..325e73640a 100644 --- a/src/ov_SC05_011/ov_SC05_011_jr_8012ACE0.c +++ b/src/ov_SC05_011/ov_SC05_011_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_011/ov_SC05_011_jr_80135D20.c b/src/ov_SC05_011/ov_SC05_011_jr_80135D20.c index 37a8e9484e..f191ecc810 100644 --- a/src/ov_SC05_011/ov_SC05_011_jr_80135D20.c +++ b/src/ov_SC05_011/ov_SC05_011_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_011/ov_SC05_011_jr_80140608.c b/src/ov_SC05_011/ov_SC05_011_jr_80140608.c index 036be8e5ca..4318050644 100644 --- a/src/ov_SC05_011/ov_SC05_011_jr_80140608.c +++ b/src/ov_SC05_011/ov_SC05_011_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017F414[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017F414[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_011/ov_SC05_011_jr_80154C24.c b/src/ov_SC05_011/ov_SC05_011_jr_80154C24.c index 663fbbc4fa..21cbb7fa04 100644 --- a/src/ov_SC05_011/ov_SC05_011_jr_80154C24.c +++ b/src/ov_SC05_011/ov_SC05_011_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_011/ov_SC05_011_jr_8015C32C.c b/src/ov_SC05_011/ov_SC05_011_jr_8015C32C.c index f787a4579b..33be7adda1 100644 --- a/src/ov_SC05_011/ov_SC05_011_jr_8015C32C.c +++ b/src/ov_SC05_011/ov_SC05_011_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801807D0[]; + extern unsigned short D_801807D2[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801807D0[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801807D2[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80180800; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80180800)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_011/ov_SC05_011_jr_8016AB6C.c b/src/ov_SC05_011/ov_SC05_011_jr_8016AB6C.c index 897cc593c6..b5f1edc733 100644 --- a/src/ov_SC05_011/ov_SC05_011_jr_8016AB6C.c +++ b/src/ov_SC05_011/ov_SC05_011_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_011/nonmatchings/ov_SC05_011_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80181248[])(void); diff --git a/src/ov_SC05_017/ov_SC05_017_after.c b/src/ov_SC05_017/ov_SC05_017_after.c index bc9eccdfb5..5e8d59d86d 100644 --- a/src/ov_SC05_017/ov_SC05_017_after.c +++ b/src/ov_SC05_017/ov_SC05_017_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_017/ov_SC05_017_jr_8012ACE0.c b/src/ov_SC05_017/ov_SC05_017_jr_8012ACE0.c index 025f580293..e9aee66f44 100644 --- a/src/ov_SC05_017/ov_SC05_017_jr_8012ACE0.c +++ b/src/ov_SC05_017/ov_SC05_017_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_017/ov_SC05_017_jr_80135D20.c b/src/ov_SC05_017/ov_SC05_017_jr_80135D20.c index ace0a3325a..d80a3ca497 100644 --- a/src/ov_SC05_017/ov_SC05_017_jr_80135D20.c +++ b/src/ov_SC05_017/ov_SC05_017_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_017/ov_SC05_017_jr_80140608.c b/src/ov_SC05_017/ov_SC05_017_jr_80140608.c index bb2456e7ec..535486e6e4 100644 --- a/src/ov_SC05_017/ov_SC05_017_jr_80140608.c +++ b/src/ov_SC05_017/ov_SC05_017_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018E8D4[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018E8D4[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_017/ov_SC05_017_jr_80154C24.c b/src/ov_SC05_017/ov_SC05_017_jr_80154C24.c index b864eea36f..6f6d492864 100644 --- a/src/ov_SC05_017/ov_SC05_017_jr_80154C24.c +++ b/src/ov_SC05_017/ov_SC05_017_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_017/ov_SC05_017_jr_8015C32C.c b/src/ov_SC05_017/ov_SC05_017_jr_8015C32C.c index 4f07e2e116..9ea34c6111 100644 --- a/src/ov_SC05_017/ov_SC05_017_jr_8015C32C.c +++ b/src/ov_SC05_017/ov_SC05_017_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018FC90[]; + extern unsigned short D_8018FC92[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018FC90[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018FC92[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018FCC0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018FCC0)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_017/ov_SC05_017_jr_8016AB6C.c b/src/ov_SC05_017/ov_SC05_017_jr_8016AB6C.c index a5b870da3b..4349daed8e 100644 --- a/src/ov_SC05_017/ov_SC05_017_jr_8016AB6C.c +++ b/src/ov_SC05_017/ov_SC05_017_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_017/nonmatchings/ov_SC05_017_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80190708[])(void); diff --git a/src/ov_SC05_018/ov_SC05_018_after.c b/src/ov_SC05_018/ov_SC05_018_after.c index b28594e4fe..baa18017f9 100644 --- a/src/ov_SC05_018/ov_SC05_018_after.c +++ b/src/ov_SC05_018/ov_SC05_018_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_018/ov_SC05_018_jr_8012ACE0.c b/src/ov_SC05_018/ov_SC05_018_jr_8012ACE0.c index 4e6dfcf959..3068b8081f 100644 --- a/src/ov_SC05_018/ov_SC05_018_jr_8012ACE0.c +++ b/src/ov_SC05_018/ov_SC05_018_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_018/ov_SC05_018_jr_80135D20.c b/src/ov_SC05_018/ov_SC05_018_jr_80135D20.c index b66c5cb97b..7d751bfb82 100644 --- a/src/ov_SC05_018/ov_SC05_018_jr_80135D20.c +++ b/src/ov_SC05_018/ov_SC05_018_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_018/ov_SC05_018_jr_80140608.c b/src/ov_SC05_018/ov_SC05_018_jr_80140608.c index ebc21f6599..1ea4aff209 100644 --- a/src/ov_SC05_018/ov_SC05_018_jr_80140608.c +++ b/src/ov_SC05_018/ov_SC05_018_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80188030[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80188030[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_018/ov_SC05_018_jr_80154C24.c b/src/ov_SC05_018/ov_SC05_018_jr_80154C24.c index 1f11dcbb96..302b499442 100644 --- a/src/ov_SC05_018/ov_SC05_018_jr_80154C24.c +++ b/src/ov_SC05_018/ov_SC05_018_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_018/ov_SC05_018_jr_8015C32C.c b/src/ov_SC05_018/ov_SC05_018_jr_8015C32C.c index 4bdede54ab..6cd2b86433 100644 --- a/src/ov_SC05_018/ov_SC05_018_jr_8015C32C.c +++ b/src/ov_SC05_018/ov_SC05_018_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801893EC[]; + extern unsigned short D_801893EE[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801893EC[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801893EE[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018941C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018941C)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_018/ov_SC05_018_jr_8016AB6C.c b/src/ov_SC05_018/ov_SC05_018_jr_8016AB6C.c index 0fcbaee0ba..259435eb84 100644 --- a/src/ov_SC05_018/ov_SC05_018_jr_8016AB6C.c +++ b/src/ov_SC05_018/ov_SC05_018_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_018/nonmatchings/ov_SC05_018_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80189E64[])(void); diff --git a/src/ov_SC05_019/ov_SC05_019_after.c b/src/ov_SC05_019/ov_SC05_019_after.c index 8d263161ad..c2dd4a5cb5 100644 --- a/src/ov_SC05_019/ov_SC05_019_after.c +++ b/src/ov_SC05_019/ov_SC05_019_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC05_019/ov_SC05_019_jr_8012ACE0.c b/src/ov_SC05_019/ov_SC05_019_jr_8012ACE0.c index 131fbbf6a9..c33d484bf1 100644 --- a/src/ov_SC05_019/ov_SC05_019_jr_8012ACE0.c +++ b/src/ov_SC05_019/ov_SC05_019_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC05_019/ov_SC05_019_jr_80135D20.c b/src/ov_SC05_019/ov_SC05_019_jr_80135D20.c index 332526bac6..a8e719fab2 100644 --- a/src/ov_SC05_019/ov_SC05_019_jr_80135D20.c +++ b/src/ov_SC05_019/ov_SC05_019_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC05_019/ov_SC05_019_jr_80140608.c b/src/ov_SC05_019/ov_SC05_019_jr_80140608.c index a93d15eeae..0d821de3b7 100644 --- a/src/ov_SC05_019/ov_SC05_019_jr_80140608.c +++ b/src/ov_SC05_019/ov_SC05_019_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80180974[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80180974[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC05_019/ov_SC05_019_jr_80154C24.c b/src/ov_SC05_019/ov_SC05_019_jr_80154C24.c index 1433276013..db1d47ccb6 100644 --- a/src/ov_SC05_019/ov_SC05_019_jr_80154C24.c +++ b/src/ov_SC05_019/ov_SC05_019_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC05_019/ov_SC05_019_jr_8015C32C.c b/src/ov_SC05_019/ov_SC05_019_jr_8015C32C.c index 3d3bb4e385..0d6b06d1bd 100644 --- a/src/ov_SC05_019/ov_SC05_019_jr_8015C32C.c +++ b/src/ov_SC05_019/ov_SC05_019_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80181D30[]; + extern unsigned short D_80181D32[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80181D30[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80181D32[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80181D60; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80181D60)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC05_019/ov_SC05_019_jr_8016AB6C.c b/src/ov_SC05_019/ov_SC05_019_jr_8016AB6C.c index eb8c5bb98d..8e87c84b09 100644 --- a/src/ov_SC05_019/ov_SC05_019_jr_8016AB6C.c +++ b/src/ov_SC05_019/ov_SC05_019_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC05_019/nonmatchings/ov_SC05_019_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801827A8[])(void); diff --git a/src/ov_SC06_000/ov_SC06_000_after.c b/src/ov_SC06_000/ov_SC06_000_after.c index ac858fd455..f017593a2f 100644 --- a/src/ov_SC06_000/ov_SC06_000_after.c +++ b/src/ov_SC06_000/ov_SC06_000_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_000/ov_SC06_000_jr_8012ACE0.c b/src/ov_SC06_000/ov_SC06_000_jr_8012ACE0.c index f97ae7be0e..4e8442c0e6 100644 --- a/src/ov_SC06_000/ov_SC06_000_jr_8012ACE0.c +++ b/src/ov_SC06_000/ov_SC06_000_jr_8012ACE0.c @@ -990,7 +990,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1900,9 +2035,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_000/ov_SC06_000_jr_80135D20.c b/src/ov_SC06_000/ov_SC06_000_jr_80135D20.c index d5d246b64e..8170191e06 100644 --- a/src/ov_SC06_000/ov_SC06_000_jr_80135D20.c +++ b/src/ov_SC06_000/ov_SC06_000_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_000/ov_SC06_000_jr_80140608.c b/src/ov_SC06_000/ov_SC06_000_jr_80140608.c index 7a8619c2ab..1032b28827 100644 --- a/src/ov_SC06_000/ov_SC06_000_jr_80140608.c +++ b/src/ov_SC06_000/ov_SC06_000_jr_80140608.c @@ -1861,7 +1861,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80189984[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80189984[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_000/ov_SC06_000_jr_80154C24.c b/src/ov_SC06_000/ov_SC06_000_jr_80154C24.c index e1ce6d284e..48b7e86c55 100644 --- a/src/ov_SC06_000/ov_SC06_000_jr_80154C24.c +++ b/src/ov_SC06_000/ov_SC06_000_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_000/ov_SC06_000_jr_8015C32C.c b/src/ov_SC06_000/ov_SC06_000_jr_8015C32C.c index 1cbc6ec435..66546716c4 100644 --- a/src/ov_SC06_000/ov_SC06_000_jr_8015C32C.c +++ b/src/ov_SC06_000/ov_SC06_000_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018AD40[]; + extern unsigned short D_8018AD42[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018AD40[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018AD42[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018AD70; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018AD70)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_000/ov_SC06_000_jr_8016AB6C.c b/src/ov_SC06_000/ov_SC06_000_jr_8016AB6C.c index 50291bdaca..533dbfeb71 100644 --- a/src/ov_SC06_000/ov_SC06_000_jr_8016AB6C.c +++ b/src/ov_SC06_000/ov_SC06_000_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_000/nonmatchings/ov_SC06_000_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018B7B8[])(void); diff --git a/src/ov_SC06_006/ov_SC06_006_after.c b/src/ov_SC06_006/ov_SC06_006_after.c index f663260504..c0d727a83e 100644 --- a/src/ov_SC06_006/ov_SC06_006_after.c +++ b/src/ov_SC06_006/ov_SC06_006_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_006/ov_SC06_006_jr_8012ACE0.c b/src/ov_SC06_006/ov_SC06_006_jr_8012ACE0.c index 735b981ba3..cb6a8c6f80 100644 --- a/src/ov_SC06_006/ov_SC06_006_jr_8012ACE0.c +++ b/src/ov_SC06_006/ov_SC06_006_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_006/ov_SC06_006_jr_80135D20.c b/src/ov_SC06_006/ov_SC06_006_jr_80135D20.c index eba7b8a8ee..4ff82a1439 100644 --- a/src/ov_SC06_006/ov_SC06_006_jr_80135D20.c +++ b/src/ov_SC06_006/ov_SC06_006_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_006/ov_SC06_006_jr_80140608.c b/src/ov_SC06_006/ov_SC06_006_jr_80140608.c index 3a288aae3e..e179a53671 100644 --- a/src/ov_SC06_006/ov_SC06_006_jr_80140608.c +++ b/src/ov_SC06_006/ov_SC06_006_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801834A8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801834A8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_006/ov_SC06_006_jr_80154C24.c b/src/ov_SC06_006/ov_SC06_006_jr_80154C24.c index 6c5f83f60e..8326f2208b 100644 --- a/src/ov_SC06_006/ov_SC06_006_jr_80154C24.c +++ b/src/ov_SC06_006/ov_SC06_006_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_006/ov_SC06_006_jr_8015C32C.c b/src/ov_SC06_006/ov_SC06_006_jr_8015C32C.c index 416e4c731a..57f3394bf5 100644 --- a/src/ov_SC06_006/ov_SC06_006_jr_8015C32C.c +++ b/src/ov_SC06_006/ov_SC06_006_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184864[]; + extern unsigned short D_80184866[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184864[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184866[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184894; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184894)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_006/ov_SC06_006_jr_8016AB6C.c b/src/ov_SC06_006/ov_SC06_006_jr_8016AB6C.c index 1ac664d446..ff6d8cb813 100644 --- a/src/ov_SC06_006/ov_SC06_006_jr_8016AB6C.c +++ b/src/ov_SC06_006/ov_SC06_006_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_006/nonmatchings/ov_SC06_006_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801852DC[])(void); diff --git a/src/ov_SC06_008/ov_SC06_008_after.c b/src/ov_SC06_008/ov_SC06_008_after.c index e82e57d1db..c507f5fb88 100644 --- a/src/ov_SC06_008/ov_SC06_008_after.c +++ b/src/ov_SC06_008/ov_SC06_008_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_008/ov_SC06_008_jr_8012ACE0.c b/src/ov_SC06_008/ov_SC06_008_jr_8012ACE0.c index b17f50de74..a0f32237d4 100644 --- a/src/ov_SC06_008/ov_SC06_008_jr_8012ACE0.c +++ b/src/ov_SC06_008/ov_SC06_008_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_008/ov_SC06_008_jr_80135D20.c b/src/ov_SC06_008/ov_SC06_008_jr_80135D20.c index fa07430803..6bc1f7f55c 100644 --- a/src/ov_SC06_008/ov_SC06_008_jr_80135D20.c +++ b/src/ov_SC06_008/ov_SC06_008_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_008/ov_SC06_008_jr_80140608.c b/src/ov_SC06_008/ov_SC06_008_jr_80140608.c index a87c70abcb..e39b07cb31 100644 --- a/src/ov_SC06_008/ov_SC06_008_jr_80140608.c +++ b/src/ov_SC06_008/ov_SC06_008_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80186F10[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80186F10[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_008/ov_SC06_008_jr_80154C24.c b/src/ov_SC06_008/ov_SC06_008_jr_80154C24.c index 84f7908516..94fef7f066 100644 --- a/src/ov_SC06_008/ov_SC06_008_jr_80154C24.c +++ b/src/ov_SC06_008/ov_SC06_008_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_008/ov_SC06_008_jr_8015C32C.c b/src/ov_SC06_008/ov_SC06_008_jr_8015C32C.c index a489340ff8..94b5b06890 100644 --- a/src/ov_SC06_008/ov_SC06_008_jr_8015C32C.c +++ b/src/ov_SC06_008/ov_SC06_008_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801882CC[]; + extern unsigned short D_801882CE[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801882CC[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801882CE[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801882FC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801882FC)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_008/ov_SC06_008_jr_8016AB6C.c b/src/ov_SC06_008/ov_SC06_008_jr_8016AB6C.c index 7faccb427a..54dc0bd3ed 100644 --- a/src/ov_SC06_008/ov_SC06_008_jr_8016AB6C.c +++ b/src/ov_SC06_008/ov_SC06_008_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_008/nonmatchings/ov_SC06_008_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80188D44[])(void); diff --git a/src/ov_SC06_010/ov_SC06_010_after.c b/src/ov_SC06_010/ov_SC06_010_after.c index 82316e39b8..c415c4fd47 100644 --- a/src/ov_SC06_010/ov_SC06_010_after.c +++ b/src/ov_SC06_010/ov_SC06_010_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_010/ov_SC06_010_jr_8012ACE0.c b/src/ov_SC06_010/ov_SC06_010_jr_8012ACE0.c index 14f0c069ad..40ac9d8d55 100644 --- a/src/ov_SC06_010/ov_SC06_010_jr_8012ACE0.c +++ b/src/ov_SC06_010/ov_SC06_010_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_010/ov_SC06_010_jr_80135D20.c b/src/ov_SC06_010/ov_SC06_010_jr_80135D20.c index 9d45e2917b..a31d6f8c00 100644 --- a/src/ov_SC06_010/ov_SC06_010_jr_80135D20.c +++ b/src/ov_SC06_010/ov_SC06_010_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_010/ov_SC06_010_jr_80140608.c b/src/ov_SC06_010/ov_SC06_010_jr_80140608.c index d067bf8abe..a22e4bff79 100644 --- a/src/ov_SC06_010/ov_SC06_010_jr_80140608.c +++ b/src/ov_SC06_010/ov_SC06_010_jr_80140608.c @@ -1843,7 +1843,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801880EC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801880EC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_010/ov_SC06_010_jr_80154C24.c b/src/ov_SC06_010/ov_SC06_010_jr_80154C24.c index 5b52216d95..631d11d595 100644 --- a/src/ov_SC06_010/ov_SC06_010_jr_80154C24.c +++ b/src/ov_SC06_010/ov_SC06_010_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_010/ov_SC06_010_jr_8015C32C.c b/src/ov_SC06_010/ov_SC06_010_jr_8015C32C.c index 8eef622897..360e8745c6 100644 --- a/src/ov_SC06_010/ov_SC06_010_jr_8015C32C.c +++ b/src/ov_SC06_010/ov_SC06_010_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801894A8[]; + extern unsigned short D_801894AA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801894A8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801894AA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801894D8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801894D8)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_010/ov_SC06_010_jr_8016AB6C.c b/src/ov_SC06_010/ov_SC06_010_jr_8016AB6C.c index 90a344d476..434ec81e4c 100644 --- a/src/ov_SC06_010/ov_SC06_010_jr_8016AB6C.c +++ b/src/ov_SC06_010/ov_SC06_010_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_010/nonmatchings/ov_SC06_010_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80189F20[])(void); diff --git a/src/ov_SC06_011/ov_SC06_011_after.c b/src/ov_SC06_011/ov_SC06_011_after.c index 777c61f2f0..5112a7f542 100644 --- a/src/ov_SC06_011/ov_SC06_011_after.c +++ b/src/ov_SC06_011/ov_SC06_011_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_011/ov_SC06_011_jr_8012ACE0.c b/src/ov_SC06_011/ov_SC06_011_jr_8012ACE0.c index 064c4c9532..7f0da6d12c 100644 --- a/src/ov_SC06_011/ov_SC06_011_jr_8012ACE0.c +++ b/src/ov_SC06_011/ov_SC06_011_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_011/ov_SC06_011_jr_80135D20.c b/src/ov_SC06_011/ov_SC06_011_jr_80135D20.c index fe36faf248..895cc3a662 100644 --- a/src/ov_SC06_011/ov_SC06_011_jr_80135D20.c +++ b/src/ov_SC06_011/ov_SC06_011_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_011/ov_SC06_011_jr_80140608.c b/src/ov_SC06_011/ov_SC06_011_jr_80140608.c index e7d5577a23..96b615338e 100644 --- a/src/ov_SC06_011/ov_SC06_011_jr_80140608.c +++ b/src/ov_SC06_011/ov_SC06_011_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80181B4C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80181B4C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_011/ov_SC06_011_jr_80154C24.c b/src/ov_SC06_011/ov_SC06_011_jr_80154C24.c index 3bbe27c33d..d7ab3c1bee 100644 --- a/src/ov_SC06_011/ov_SC06_011_jr_80154C24.c +++ b/src/ov_SC06_011/ov_SC06_011_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_011/ov_SC06_011_jr_8015C32C.c b/src/ov_SC06_011/ov_SC06_011_jr_8015C32C.c index 2678026b89..5694be0a12 100644 --- a/src/ov_SC06_011/ov_SC06_011_jr_8015C32C.c +++ b/src/ov_SC06_011/ov_SC06_011_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80182F08[]; + extern unsigned short D_80182F0A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80182F08[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80182F0A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80182F38; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80182F38)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_011/ov_SC06_011_jr_8016AB6C.c b/src/ov_SC06_011/ov_SC06_011_jr_8016AB6C.c index a0417b761d..50eeb2b529 100644 --- a/src/ov_SC06_011/ov_SC06_011_jr_8016AB6C.c +++ b/src/ov_SC06_011/ov_SC06_011_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_011/nonmatchings/ov_SC06_011_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80183980[])(void); diff --git a/src/ov_SC06_013/ov_SC06_013_after.c b/src/ov_SC06_013/ov_SC06_013_after.c index ff41eabaed..dbf252ac56 100644 --- a/src/ov_SC06_013/ov_SC06_013_after.c +++ b/src/ov_SC06_013/ov_SC06_013_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_013/ov_SC06_013_jr_8012ACE0.c b/src/ov_SC06_013/ov_SC06_013_jr_8012ACE0.c index ec9fbb4ab9..e18a948adf 100644 --- a/src/ov_SC06_013/ov_SC06_013_jr_8012ACE0.c +++ b/src/ov_SC06_013/ov_SC06_013_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_013/ov_SC06_013_jr_80135D20.c b/src/ov_SC06_013/ov_SC06_013_jr_80135D20.c index b39c21731b..d94deb5ae9 100644 --- a/src/ov_SC06_013/ov_SC06_013_jr_80135D20.c +++ b/src/ov_SC06_013/ov_SC06_013_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_013/ov_SC06_013_jr_80140608.c b/src/ov_SC06_013/ov_SC06_013_jr_80140608.c index 12ba4828b9..e1a25158c3 100644 --- a/src/ov_SC06_013/ov_SC06_013_jr_80140608.c +++ b/src/ov_SC06_013/ov_SC06_013_jr_80140608.c @@ -1810,7 +1810,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_801802C8[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801802C8[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_013/ov_SC06_013_jr_80154C24.c b/src/ov_SC06_013/ov_SC06_013_jr_80154C24.c index 76952c72ab..8cb346ff7b 100644 --- a/src/ov_SC06_013/ov_SC06_013_jr_80154C24.c +++ b/src/ov_SC06_013/ov_SC06_013_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_013/ov_SC06_013_jr_8015C32C.c b/src/ov_SC06_013/ov_SC06_013_jr_8015C32C.c index 280946fc07..7658bc28f3 100644 --- a/src/ov_SC06_013/ov_SC06_013_jr_8015C32C.c +++ b/src/ov_SC06_013/ov_SC06_013_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80181684[]; + extern unsigned short D_80181686[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80181684[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80181686[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801816B4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801816B4)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_013/ov_SC06_013_jr_8016AB6C.c b/src/ov_SC06_013/ov_SC06_013_jr_8016AB6C.c index 3207735887..f3a288fdab 100644 --- a/src/ov_SC06_013/ov_SC06_013_jr_8016AB6C.c +++ b/src/ov_SC06_013/ov_SC06_013_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_013/nonmatchings/ov_SC06_013_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801820FC[])(void); diff --git a/src/ov_SC06_014/ov_SC06_014_after.c b/src/ov_SC06_014/ov_SC06_014_after.c index 897e0ec528..09ba9ecc55 100644 --- a/src/ov_SC06_014/ov_SC06_014_after.c +++ b/src/ov_SC06_014/ov_SC06_014_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_014/ov_SC06_014_jr_8012ACE0.c b/src/ov_SC06_014/ov_SC06_014_jr_8012ACE0.c index 7c19305545..15758ece2e 100644 --- a/src/ov_SC06_014/ov_SC06_014_jr_8012ACE0.c +++ b/src/ov_SC06_014/ov_SC06_014_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_014/ov_SC06_014_jr_80135D20.c b/src/ov_SC06_014/ov_SC06_014_jr_80135D20.c index fa6805588f..b8d177f606 100644 --- a/src/ov_SC06_014/ov_SC06_014_jr_80135D20.c +++ b/src/ov_SC06_014/ov_SC06_014_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_014/ov_SC06_014_jr_80140608.c b/src/ov_SC06_014/ov_SC06_014_jr_80140608.c index 2372f1b455..8207e56574 100644 --- a/src/ov_SC06_014/ov_SC06_014_jr_80140608.c +++ b/src/ov_SC06_014/ov_SC06_014_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018162C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018162C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_014/ov_SC06_014_jr_80154C24.c b/src/ov_SC06_014/ov_SC06_014_jr_80154C24.c index 124e70f630..959ccdc448 100644 --- a/src/ov_SC06_014/ov_SC06_014_jr_80154C24.c +++ b/src/ov_SC06_014/ov_SC06_014_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_014/ov_SC06_014_jr_8015C32C.c b/src/ov_SC06_014/ov_SC06_014_jr_8015C32C.c index d43d2a53c5..f3f4f52e32 100644 --- a/src/ov_SC06_014/ov_SC06_014_jr_8015C32C.c +++ b/src/ov_SC06_014/ov_SC06_014_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801829E8[]; + extern unsigned short D_801829EA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801829E8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801829EA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80182A18; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80182A18)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_014/ov_SC06_014_jr_8016AB6C.c b/src/ov_SC06_014/ov_SC06_014_jr_8016AB6C.c index 9ec41f3578..459c8ab0f5 100644 --- a/src/ov_SC06_014/ov_SC06_014_jr_8016AB6C.c +++ b/src/ov_SC06_014/ov_SC06_014_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_014/nonmatchings/ov_SC06_014_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80183460[])(void); diff --git a/src/ov_SC06_015/ov_SC06_015_after.c b/src/ov_SC06_015/ov_SC06_015_after.c index 889866b302..d3f07c9c0b 100644 --- a/src/ov_SC06_015/ov_SC06_015_after.c +++ b/src/ov_SC06_015/ov_SC06_015_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_015/ov_SC06_015_jr_8012ACE0.c b/src/ov_SC06_015/ov_SC06_015_jr_8012ACE0.c index 0d0a301d84..76cc4c2924 100644 --- a/src/ov_SC06_015/ov_SC06_015_jr_8012ACE0.c +++ b/src/ov_SC06_015/ov_SC06_015_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_015/ov_SC06_015_jr_80135D20.c b/src/ov_SC06_015/ov_SC06_015_jr_80135D20.c index 12724cd2f9..bc527e85fc 100644 --- a/src/ov_SC06_015/ov_SC06_015_jr_80135D20.c +++ b/src/ov_SC06_015/ov_SC06_015_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_015/ov_SC06_015_jr_80140608.c b/src/ov_SC06_015/ov_SC06_015_jr_80140608.c index 6a14b147ac..29d12c0705 100644 --- a/src/ov_SC06_015/ov_SC06_015_jr_80140608.c +++ b/src/ov_SC06_015/ov_SC06_015_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80180184[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80180184[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_015/ov_SC06_015_jr_80154C24.c b/src/ov_SC06_015/ov_SC06_015_jr_80154C24.c index 1081f1d099..6d6cabe43a 100644 --- a/src/ov_SC06_015/ov_SC06_015_jr_80154C24.c +++ b/src/ov_SC06_015/ov_SC06_015_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_015/ov_SC06_015_jr_8015C32C.c b/src/ov_SC06_015/ov_SC06_015_jr_8015C32C.c index 4ff6715778..47caa247fa 100644 --- a/src/ov_SC06_015/ov_SC06_015_jr_8015C32C.c +++ b/src/ov_SC06_015/ov_SC06_015_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80181540[]; + extern unsigned short D_80181542[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80181540[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80181542[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80181570; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80181570)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_015/ov_SC06_015_jr_8016AB6C.c b/src/ov_SC06_015/ov_SC06_015_jr_8016AB6C.c index 5f335b3c58..ab6736df98 100644 --- a/src/ov_SC06_015/ov_SC06_015_jr_8016AB6C.c +++ b/src/ov_SC06_015/ov_SC06_015_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_015/nonmatchings/ov_SC06_015_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80181FB8[])(void); diff --git a/src/ov_SC06_016/ov_SC06_016_after.c b/src/ov_SC06_016/ov_SC06_016_after.c index f2519dfb55..7f86b513de 100644 --- a/src/ov_SC06_016/ov_SC06_016_after.c +++ b/src/ov_SC06_016/ov_SC06_016_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_016/ov_SC06_016_jr_8012ACE0.c b/src/ov_SC06_016/ov_SC06_016_jr_8012ACE0.c index ef73e52224..ec6dea231d 100644 --- a/src/ov_SC06_016/ov_SC06_016_jr_8012ACE0.c +++ b/src/ov_SC06_016/ov_SC06_016_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_016/ov_SC06_016_jr_80135D20.c b/src/ov_SC06_016/ov_SC06_016_jr_80135D20.c index a24da569cc..22e8767d17 100644 --- a/src/ov_SC06_016/ov_SC06_016_jr_80135D20.c +++ b/src/ov_SC06_016/ov_SC06_016_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_016/ov_SC06_016_jr_80140608.c b/src/ov_SC06_016/ov_SC06_016_jr_80140608.c index c0b9267532..f619686323 100644 --- a/src/ov_SC06_016/ov_SC06_016_jr_80140608.c +++ b/src/ov_SC06_016/ov_SC06_016_jr_80140608.c @@ -1825,7 +1825,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80186594[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80186594[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_016/ov_SC06_016_jr_80154C24.c b/src/ov_SC06_016/ov_SC06_016_jr_80154C24.c index 00bc9aa5d9..2c280cf249 100644 --- a/src/ov_SC06_016/ov_SC06_016_jr_80154C24.c +++ b/src/ov_SC06_016/ov_SC06_016_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_016/ov_SC06_016_jr_8015C32C.c b/src/ov_SC06_016/ov_SC06_016_jr_8015C32C.c index 43fbdb5d96..65ac093a35 100644 --- a/src/ov_SC06_016/ov_SC06_016_jr_8015C32C.c +++ b/src/ov_SC06_016/ov_SC06_016_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80187950[]; + extern unsigned short D_80187952[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80187950[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80187952[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80187980; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80187980)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_016/ov_SC06_016_jr_8016AB6C.c b/src/ov_SC06_016/ov_SC06_016_jr_8016AB6C.c index f23bad5b77..cef59ad805 100644 --- a/src/ov_SC06_016/ov_SC06_016_jr_8016AB6C.c +++ b/src/ov_SC06_016/ov_SC06_016_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_016/nonmatchings/ov_SC06_016_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801883C8[])(void); diff --git a/src/ov_SC06_018/ov_SC06_018_after.c b/src/ov_SC06_018/ov_SC06_018_after.c index a5fdb65b3a..f1b4715830 100644 --- a/src/ov_SC06_018/ov_SC06_018_after.c +++ b/src/ov_SC06_018/ov_SC06_018_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_018/ov_SC06_018_jr_8012ACE0.c b/src/ov_SC06_018/ov_SC06_018_jr_8012ACE0.c index cf65be655e..d481aebb50 100644 --- a/src/ov_SC06_018/ov_SC06_018_jr_8012ACE0.c +++ b/src/ov_SC06_018/ov_SC06_018_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_018/ov_SC06_018_jr_80135D20.c b/src/ov_SC06_018/ov_SC06_018_jr_80135D20.c index 9801f9d42d..e44821d74a 100644 --- a/src/ov_SC06_018/ov_SC06_018_jr_80135D20.c +++ b/src/ov_SC06_018/ov_SC06_018_jr_80135D20.c @@ -1445,13 +1445,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_018/ov_SC06_018_jr_80140608.c b/src/ov_SC06_018/ov_SC06_018_jr_80140608.c index 72fac3f4aa..d4c3a6f637 100644 --- a/src/ov_SC06_018/ov_SC06_018_jr_80140608.c +++ b/src/ov_SC06_018/ov_SC06_018_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80194CD0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80194CD0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_018/ov_SC06_018_jr_80154C24.c b/src/ov_SC06_018/ov_SC06_018_jr_80154C24.c index 51eceebc50..0bb7a3145d 100644 --- a/src/ov_SC06_018/ov_SC06_018_jr_80154C24.c +++ b/src/ov_SC06_018/ov_SC06_018_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_018/ov_SC06_018_jr_8015C32C.c b/src/ov_SC06_018/ov_SC06_018_jr_8015C32C.c index f3a217b564..81f8500bc7 100644 --- a/src/ov_SC06_018/ov_SC06_018_jr_8015C32C.c +++ b/src/ov_SC06_018/ov_SC06_018_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8019608C[]; + extern unsigned short D_8019608E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8019608C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8019608E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801960BC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801960BC)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_018/ov_SC06_018_jr_8016AB6C.c b/src/ov_SC06_018/ov_SC06_018_jr_8016AB6C.c index 1f07747b54..15e3fe7d98 100644 --- a/src/ov_SC06_018/ov_SC06_018_jr_8016AB6C.c +++ b/src/ov_SC06_018/ov_SC06_018_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_018/nonmatchings/ov_SC06_018_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80196B04[])(void); diff --git a/src/ov_SC06_020/ov_SC06_020_after.c b/src/ov_SC06_020/ov_SC06_020_after.c index 7b33fa69cf..561c68bddd 100644 --- a/src/ov_SC06_020/ov_SC06_020_after.c +++ b/src/ov_SC06_020/ov_SC06_020_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_020/ov_SC06_020_jr_8012ACE0.c b/src/ov_SC06_020/ov_SC06_020_jr_8012ACE0.c index fd6ca712a5..2dffcd876c 100644 --- a/src/ov_SC06_020/ov_SC06_020_jr_8012ACE0.c +++ b/src/ov_SC06_020/ov_SC06_020_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_020/ov_SC06_020_jr_80135D20.c b/src/ov_SC06_020/ov_SC06_020_jr_80135D20.c index 6f3fc44a84..b5205116af 100644 --- a/src/ov_SC06_020/ov_SC06_020_jr_80135D20.c +++ b/src/ov_SC06_020/ov_SC06_020_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_020/ov_SC06_020_jr_80140608.c b/src/ov_SC06_020/ov_SC06_020_jr_80140608.c index 018286b3c2..566a48b361 100644 --- a/src/ov_SC06_020/ov_SC06_020_jr_80140608.c +++ b/src/ov_SC06_020/ov_SC06_020_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80186758[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80186758[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_020/ov_SC06_020_jr_80154C24.c b/src/ov_SC06_020/ov_SC06_020_jr_80154C24.c index 756ff7b247..4074176bd9 100644 --- a/src/ov_SC06_020/ov_SC06_020_jr_80154C24.c +++ b/src/ov_SC06_020/ov_SC06_020_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_020/ov_SC06_020_jr_8015C32C.c b/src/ov_SC06_020/ov_SC06_020_jr_8015C32C.c index 09a4c30ee5..bb7ae3d878 100644 --- a/src/ov_SC06_020/ov_SC06_020_jr_8015C32C.c +++ b/src/ov_SC06_020/ov_SC06_020_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80187B14[]; + extern unsigned short D_80187B16[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80187B14[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80187B16[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80187B44; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80187B44)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_020/ov_SC06_020_jr_8016AB6C.c b/src/ov_SC06_020/ov_SC06_020_jr_8016AB6C.c index 38b7bbd41d..314a7d9973 100644 --- a/src/ov_SC06_020/ov_SC06_020_jr_8016AB6C.c +++ b/src/ov_SC06_020/ov_SC06_020_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_020/nonmatchings/ov_SC06_020_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018858C[])(void); diff --git a/src/ov_SC06_022/ov_SC06_022_after.c b/src/ov_SC06_022/ov_SC06_022_after.c index 265e06ff6c..f3a2c76b63 100644 --- a/src/ov_SC06_022/ov_SC06_022_after.c +++ b/src/ov_SC06_022/ov_SC06_022_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_022/ov_SC06_022_jr_8012ACE0.c b/src/ov_SC06_022/ov_SC06_022_jr_8012ACE0.c index c12d17debf..28c002c6b1 100644 --- a/src/ov_SC06_022/ov_SC06_022_jr_8012ACE0.c +++ b/src/ov_SC06_022/ov_SC06_022_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_022/ov_SC06_022_jr_80135D20.c b/src/ov_SC06_022/ov_SC06_022_jr_80135D20.c index da513c1fa5..62f19ffa34 100644 --- a/src/ov_SC06_022/ov_SC06_022_jr_80135D20.c +++ b/src/ov_SC06_022/ov_SC06_022_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_022/ov_SC06_022_jr_80140608.c b/src/ov_SC06_022/ov_SC06_022_jr_80140608.c index 0e4f71f3f4..f7b58e113d 100644 --- a/src/ov_SC06_022/ov_SC06_022_jr_80140608.c +++ b/src/ov_SC06_022/ov_SC06_022_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018EA14[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018EA14[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_022/ov_SC06_022_jr_80154C24.c b/src/ov_SC06_022/ov_SC06_022_jr_80154C24.c index bfeec2a833..388a9477c0 100644 --- a/src/ov_SC06_022/ov_SC06_022_jr_80154C24.c +++ b/src/ov_SC06_022/ov_SC06_022_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_022/ov_SC06_022_jr_8015C32C.c b/src/ov_SC06_022/ov_SC06_022_jr_8015C32C.c index 4e1b241a9d..2db038e773 100644 --- a/src/ov_SC06_022/ov_SC06_022_jr_8015C32C.c +++ b/src/ov_SC06_022/ov_SC06_022_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018FDD0[]; + extern unsigned short D_8018FDD2[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018FDD0[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018FDD2[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018FE00; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018FE00)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_022/ov_SC06_022_jr_8016AB6C.c b/src/ov_SC06_022/ov_SC06_022_jr_8016AB6C.c index 6aa35a3107..1957a2b034 100644 --- a/src/ov_SC06_022/ov_SC06_022_jr_8016AB6C.c +++ b/src/ov_SC06_022/ov_SC06_022_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_022/nonmatchings/ov_SC06_022_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80190848[])(void); diff --git a/src/ov_SC06_024/ov_SC06_024_after.c b/src/ov_SC06_024/ov_SC06_024_after.c index 354661c08f..60f9e87c8c 100644 --- a/src/ov_SC06_024/ov_SC06_024_after.c +++ b/src/ov_SC06_024/ov_SC06_024_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_024/ov_SC06_024_jr_8012ACE0.c b/src/ov_SC06_024/ov_SC06_024_jr_8012ACE0.c index 7c8e82f074..2f0b13c0dd 100644 --- a/src/ov_SC06_024/ov_SC06_024_jr_8012ACE0.c +++ b/src/ov_SC06_024/ov_SC06_024_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_024/ov_SC06_024_jr_80135D20.c b/src/ov_SC06_024/ov_SC06_024_jr_80135D20.c index ad57d4a6ea..a8ad0d4cf6 100644 --- a/src/ov_SC06_024/ov_SC06_024_jr_80135D20.c +++ b/src/ov_SC06_024/ov_SC06_024_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_024/ov_SC06_024_jr_80140608.c b/src/ov_SC06_024/ov_SC06_024_jr_80140608.c index 2816a7dfc8..a6041ed87c 100644 --- a/src/ov_SC06_024/ov_SC06_024_jr_80140608.c +++ b/src/ov_SC06_024/ov_SC06_024_jr_80140608.c @@ -1825,7 +1825,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80190EEC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80190EEC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_024/ov_SC06_024_jr_80154C24.c b/src/ov_SC06_024/ov_SC06_024_jr_80154C24.c index 09a13cc575..c118b1f9ac 100644 --- a/src/ov_SC06_024/ov_SC06_024_jr_80154C24.c +++ b/src/ov_SC06_024/ov_SC06_024_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_024/ov_SC06_024_jr_8015C32C.c b/src/ov_SC06_024/ov_SC06_024_jr_8015C32C.c index 23d596d7b0..1a17ce4728 100644 --- a/src/ov_SC06_024/ov_SC06_024_jr_8015C32C.c +++ b/src/ov_SC06_024/ov_SC06_024_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801922A8[]; + extern unsigned short D_801922AA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801922A8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801922AA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801922D8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801922D8)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_024/ov_SC06_024_jr_8016AB6C.c b/src/ov_SC06_024/ov_SC06_024_jr_8016AB6C.c index c58ed661ed..3b97a11b95 100644 --- a/src/ov_SC06_024/ov_SC06_024_jr_8016AB6C.c +++ b/src/ov_SC06_024/ov_SC06_024_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_024/nonmatchings/ov_SC06_024_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80192D20[])(void); diff --git a/src/ov_SC06_025/ov_SC06_025_after.c b/src/ov_SC06_025/ov_SC06_025_after.c index bcb22c1d9e..2b80e533dd 100644 --- a/src/ov_SC06_025/ov_SC06_025_after.c +++ b/src/ov_SC06_025/ov_SC06_025_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_025/ov_SC06_025_jr_8012ACE0.c b/src/ov_SC06_025/ov_SC06_025_jr_8012ACE0.c index 11e370120c..924818e366 100644 --- a/src/ov_SC06_025/ov_SC06_025_jr_8012ACE0.c +++ b/src/ov_SC06_025/ov_SC06_025_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_025/ov_SC06_025_jr_80135D20.c b/src/ov_SC06_025/ov_SC06_025_jr_80135D20.c index d474e82ef5..ab279fd5de 100644 --- a/src/ov_SC06_025/ov_SC06_025_jr_80135D20.c +++ b/src/ov_SC06_025/ov_SC06_025_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_025/ov_SC06_025_jr_80140608.c b/src/ov_SC06_025/ov_SC06_025_jr_80140608.c index c9244c8d1a..91278f3d06 100644 --- a/src/ov_SC06_025/ov_SC06_025_jr_80140608.c +++ b/src/ov_SC06_025/ov_SC06_025_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80186390[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80186390[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_025/ov_SC06_025_jr_80154C24.c b/src/ov_SC06_025/ov_SC06_025_jr_80154C24.c index e49c3fda37..b4f1ebf62d 100644 --- a/src/ov_SC06_025/ov_SC06_025_jr_80154C24.c +++ b/src/ov_SC06_025/ov_SC06_025_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_025/ov_SC06_025_jr_8015C32C.c b/src/ov_SC06_025/ov_SC06_025_jr_8015C32C.c index b519ba8586..e659da2f06 100644 --- a/src/ov_SC06_025/ov_SC06_025_jr_8015C32C.c +++ b/src/ov_SC06_025/ov_SC06_025_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018774C[]; + extern unsigned short D_8018774E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018774C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018774E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018777C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018777C)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_025/ov_SC06_025_jr_8016AB6C.c b/src/ov_SC06_025/ov_SC06_025_jr_8016AB6C.c index 4a9383ef86..19b1fea8b1 100644 --- a/src/ov_SC06_025/ov_SC06_025_jr_8016AB6C.c +++ b/src/ov_SC06_025/ov_SC06_025_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_025/nonmatchings/ov_SC06_025_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801881C4[])(void); diff --git a/src/ov_SC06_027/ov_SC06_027_after.c b/src/ov_SC06_027/ov_SC06_027_after.c index 32425f25a0..e02fef756a 100644 --- a/src/ov_SC06_027/ov_SC06_027_after.c +++ b/src/ov_SC06_027/ov_SC06_027_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_027/ov_SC06_027_jr_8012ACE0.c b/src/ov_SC06_027/ov_SC06_027_jr_8012ACE0.c index ff549149a4..f5eaf03d65 100644 --- a/src/ov_SC06_027/ov_SC06_027_jr_8012ACE0.c +++ b/src/ov_SC06_027/ov_SC06_027_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_027/ov_SC06_027_jr_80135D20.c b/src/ov_SC06_027/ov_SC06_027_jr_80135D20.c index a037772ae2..bdb2893fbd 100644 --- a/src/ov_SC06_027/ov_SC06_027_jr_80135D20.c +++ b/src/ov_SC06_027/ov_SC06_027_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_027/ov_SC06_027_jr_80140608.c b/src/ov_SC06_027/ov_SC06_027_jr_80140608.c index 81e6720605..aaec09cec0 100644 --- a/src/ov_SC06_027/ov_SC06_027_jr_80140608.c +++ b/src/ov_SC06_027/ov_SC06_027_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017F6D0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017F6D0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_027/ov_SC06_027_jr_80154C24.c b/src/ov_SC06_027/ov_SC06_027_jr_80154C24.c index d948e1516b..dd3918e92d 100644 --- a/src/ov_SC06_027/ov_SC06_027_jr_80154C24.c +++ b/src/ov_SC06_027/ov_SC06_027_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_027/ov_SC06_027_jr_8015C32C.c b/src/ov_SC06_027/ov_SC06_027_jr_8015C32C.c index 942469d111..e43f9e10b7 100644 --- a/src/ov_SC06_027/ov_SC06_027_jr_8015C32C.c +++ b/src/ov_SC06_027/ov_SC06_027_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80180A8C[]; + extern unsigned short D_80180A8E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80180A8C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80180A8E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80180ABC; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80180ABC)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_027/ov_SC06_027_jr_8016AB6C.c b/src/ov_SC06_027/ov_SC06_027_jr_8016AB6C.c index 3205183c90..7de1e1bb2e 100644 --- a/src/ov_SC06_027/ov_SC06_027_jr_8016AB6C.c +++ b/src/ov_SC06_027/ov_SC06_027_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_027/nonmatchings/ov_SC06_027_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80181504[])(void); diff --git a/src/ov_SC06_029/ov_SC06_029_after.c b/src/ov_SC06_029/ov_SC06_029_after.c index 9b4e1aeefb..1972e93ec1 100644 --- a/src/ov_SC06_029/ov_SC06_029_after.c +++ b/src/ov_SC06_029/ov_SC06_029_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_029/ov_SC06_029_jr_8012ACE0.c b/src/ov_SC06_029/ov_SC06_029_jr_8012ACE0.c index c9c77967c4..755995d6a4 100644 --- a/src/ov_SC06_029/ov_SC06_029_jr_8012ACE0.c +++ b/src/ov_SC06_029/ov_SC06_029_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_029/ov_SC06_029_jr_80135D20.c b/src/ov_SC06_029/ov_SC06_029_jr_80135D20.c index 2a31915800..c34ab76160 100644 --- a/src/ov_SC06_029/ov_SC06_029_jr_80135D20.c +++ b/src/ov_SC06_029/ov_SC06_029_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_029/ov_SC06_029_jr_80140608.c b/src/ov_SC06_029/ov_SC06_029_jr_80140608.c index 59ff0cdad8..aabe1eef48 100644 --- a/src/ov_SC06_029/ov_SC06_029_jr_80140608.c +++ b/src/ov_SC06_029/ov_SC06_029_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8018DC64[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018DC64[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_029/ov_SC06_029_jr_80154C24.c b/src/ov_SC06_029/ov_SC06_029_jr_80154C24.c index bf0b5447fd..d9830504b8 100644 --- a/src/ov_SC06_029/ov_SC06_029_jr_80154C24.c +++ b/src/ov_SC06_029/ov_SC06_029_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_029/ov_SC06_029_jr_8015C32C.c b/src/ov_SC06_029/ov_SC06_029_jr_8015C32C.c index 24574e65f3..34883970f8 100644 --- a/src/ov_SC06_029/ov_SC06_029_jr_8015C32C.c +++ b/src/ov_SC06_029/ov_SC06_029_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018F020[]; + extern unsigned short D_8018F022[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018F020[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018F022[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018F050; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018F050)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_029/ov_SC06_029_jr_8016AB6C.c b/src/ov_SC06_029/ov_SC06_029_jr_8016AB6C.c index ae976b35e3..14da45eee8 100644 --- a/src/ov_SC06_029/ov_SC06_029_jr_8016AB6C.c +++ b/src/ov_SC06_029/ov_SC06_029_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_029/nonmatchings/ov_SC06_029_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_8018FA98[])(void); diff --git a/src/ov_SC06_030/ov_SC06_030_after.c b/src/ov_SC06_030/ov_SC06_030_after.c index 3e39b6360b..b48553fb5f 100644 --- a/src/ov_SC06_030/ov_SC06_030_after.c +++ b/src/ov_SC06_030/ov_SC06_030_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_030/ov_SC06_030_jr_8012ACE0.c b/src/ov_SC06_030/ov_SC06_030_jr_8012ACE0.c index cdb429a76a..63f47e2912 100644 --- a/src/ov_SC06_030/ov_SC06_030_jr_8012ACE0.c +++ b/src/ov_SC06_030/ov_SC06_030_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_030/ov_SC06_030_jr_80135D20.c b/src/ov_SC06_030/ov_SC06_030_jr_80135D20.c index 70e055cdca..e47c4fb73b 100644 --- a/src/ov_SC06_030/ov_SC06_030_jr_80135D20.c +++ b/src/ov_SC06_030/ov_SC06_030_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_030/ov_SC06_030_jr_80140608.c b/src/ov_SC06_030/ov_SC06_030_jr_80140608.c index 70509c6272..4e7cb75b0c 100644 --- a/src/ov_SC06_030/ov_SC06_030_jr_80140608.c +++ b/src/ov_SC06_030/ov_SC06_030_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183554[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183554[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_030/ov_SC06_030_jr_80154C24.c b/src/ov_SC06_030/ov_SC06_030_jr_80154C24.c index eecee619b2..ff93e0b493 100644 --- a/src/ov_SC06_030/ov_SC06_030_jr_80154C24.c +++ b/src/ov_SC06_030/ov_SC06_030_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_030/ov_SC06_030_jr_8015C32C.c b/src/ov_SC06_030/ov_SC06_030_jr_8015C32C.c index b6e8fa6d7f..2cd55557ee 100644 --- a/src/ov_SC06_030/ov_SC06_030_jr_8015C32C.c +++ b/src/ov_SC06_030/ov_SC06_030_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184910[]; + extern unsigned short D_80184912[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184910[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184912[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184940; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184940)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_030/ov_SC06_030_jr_8016AB6C.c b/src/ov_SC06_030/ov_SC06_030_jr_8016AB6C.c index 3169e97b35..e1d5d33ff5 100644 --- a/src/ov_SC06_030/ov_SC06_030_jr_8016AB6C.c +++ b/src/ov_SC06_030/ov_SC06_030_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_030/nonmatchings/ov_SC06_030_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80185388[])(void); diff --git a/src/ov_SC06_032/ov_SC06_032_after.c b/src/ov_SC06_032/ov_SC06_032_after.c index ffabf0a06c..4599286914 100644 --- a/src/ov_SC06_032/ov_SC06_032_after.c +++ b/src/ov_SC06_032/ov_SC06_032_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_032/ov_SC06_032_jr_8012ACE0.c b/src/ov_SC06_032/ov_SC06_032_jr_8012ACE0.c index 399d12daf3..a901c4a65f 100644 --- a/src/ov_SC06_032/ov_SC06_032_jr_8012ACE0.c +++ b/src/ov_SC06_032/ov_SC06_032_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_032/ov_SC06_032_jr_80135D20.c b/src/ov_SC06_032/ov_SC06_032_jr_80135D20.c index b8b9fd1cbe..e0c7bcb6df 100644 --- a/src/ov_SC06_032/ov_SC06_032_jr_80135D20.c +++ b/src/ov_SC06_032/ov_SC06_032_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_032/ov_SC06_032_jr_80140608.c b/src/ov_SC06_032/ov_SC06_032_jr_80140608.c index 076e99f7c3..8047a259dd 100644 --- a/src/ov_SC06_032/ov_SC06_032_jr_80140608.c +++ b/src/ov_SC06_032/ov_SC06_032_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80194A20[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80194A20[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_032/ov_SC06_032_jr_80154C24.c b/src/ov_SC06_032/ov_SC06_032_jr_80154C24.c index db7d9b703c..e03b799e91 100644 --- a/src/ov_SC06_032/ov_SC06_032_jr_80154C24.c +++ b/src/ov_SC06_032/ov_SC06_032_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_032/ov_SC06_032_jr_8015C32C.c b/src/ov_SC06_032/ov_SC06_032_jr_8015C32C.c index 9eddef7d1e..6dab0b30ec 100644 --- a/src/ov_SC06_032/ov_SC06_032_jr_8015C32C.c +++ b/src/ov_SC06_032/ov_SC06_032_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80195DDC[]; + extern unsigned short D_80195DDE[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80195DDC[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80195DDE[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80195E0C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80195E0C)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_032/ov_SC06_032_jr_8016AB6C.c b/src/ov_SC06_032/ov_SC06_032_jr_8016AB6C.c index e2bdd00360..60636610bb 100644 --- a/src/ov_SC06_032/ov_SC06_032_jr_8016AB6C.c +++ b/src/ov_SC06_032/ov_SC06_032_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_032/nonmatchings/ov_SC06_032_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80196854[])(void); diff --git a/src/ov_SC06_033/ov_SC06_033_after.c b/src/ov_SC06_033/ov_SC06_033_after.c index 592ca936c5..17373538c8 100644 --- a/src/ov_SC06_033/ov_SC06_033_after.c +++ b/src/ov_SC06_033/ov_SC06_033_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC06_033/ov_SC06_033_jr_8012ACE0.c b/src/ov_SC06_033/ov_SC06_033_jr_8012ACE0.c index 903fa63798..ad3e5aa9e3 100644 --- a/src/ov_SC06_033/ov_SC06_033_jr_8012ACE0.c +++ b/src/ov_SC06_033/ov_SC06_033_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC06_033/ov_SC06_033_jr_80135D20.c b/src/ov_SC06_033/ov_SC06_033_jr_80135D20.c index 6ab7f920c8..b58c56ab48 100644 --- a/src/ov_SC06_033/ov_SC06_033_jr_80135D20.c +++ b/src/ov_SC06_033/ov_SC06_033_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC06_033/ov_SC06_033_jr_80140608.c b/src/ov_SC06_033/ov_SC06_033_jr_80140608.c index d4b087e4ea..88d488bc15 100644 --- a/src/ov_SC06_033/ov_SC06_033_jr_80140608.c +++ b/src/ov_SC06_033/ov_SC06_033_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80192F30[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80192F30[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC06_033/ov_SC06_033_jr_80154C24.c b/src/ov_SC06_033/ov_SC06_033_jr_80154C24.c index e1dee8d312..2b00db70bd 100644 --- a/src/ov_SC06_033/ov_SC06_033_jr_80154C24.c +++ b/src/ov_SC06_033/ov_SC06_033_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC06_033/ov_SC06_033_jr_8015C32C.c b/src/ov_SC06_033/ov_SC06_033_jr_8015C32C.c index 1a0e323b2c..de9e98fb7d 100644 --- a/src/ov_SC06_033/ov_SC06_033_jr_8015C32C.c +++ b/src/ov_SC06_033/ov_SC06_033_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801942EC[]; + extern unsigned short D_801942EE[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801942EC[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801942EE[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8019431C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8019431C)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC06_033/ov_SC06_033_jr_8016AB6C.c b/src/ov_SC06_033/ov_SC06_033_jr_8016AB6C.c index 7d09e06638..872c91c2a6 100644 --- a/src/ov_SC06_033/ov_SC06_033_jr_8016AB6C.c +++ b/src/ov_SC06_033/ov_SC06_033_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC06_033/nonmatchings/ov_SC06_033_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80194D64[])(void); diff --git a/src/ov_SC07_000/ov_SC07_000_after.c b/src/ov_SC07_000/ov_SC07_000_after.c index 1496449e8c..5dadb31305 100644 --- a/src/ov_SC07_000/ov_SC07_000_after.c +++ b/src/ov_SC07_000/ov_SC07_000_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC07_000/ov_SC07_000_jr_8012ACE0.c b/src/ov_SC07_000/ov_SC07_000_jr_8012ACE0.c index 8e63d98bc5..f32473fca1 100644 --- a/src/ov_SC07_000/ov_SC07_000_jr_8012ACE0.c +++ b/src/ov_SC07_000/ov_SC07_000_jr_8012ACE0.c @@ -990,7 +990,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1900,9 +2035,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC07_000/ov_SC07_000_jr_80135D20.c b/src/ov_SC07_000/ov_SC07_000_jr_80135D20.c index 5c948e1120..ee1fe285a2 100644 --- a/src/ov_SC07_000/ov_SC07_000_jr_80135D20.c +++ b/src/ov_SC07_000/ov_SC07_000_jr_80135D20.c @@ -1289,13 +1289,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC07_000/ov_SC07_000_jr_80140608.c b/src/ov_SC07_000/ov_SC07_000_jr_80140608.c index fe1ca3e0dd..d7e87e2e40 100644 --- a/src/ov_SC07_000/ov_SC07_000_jr_80140608.c +++ b/src/ov_SC07_000/ov_SC07_000_jr_80140608.c @@ -1811,7 +1811,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80183D78[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80183D78[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC07_000/ov_SC07_000_jr_80154C24.c b/src/ov_SC07_000/ov_SC07_000_jr_80154C24.c index 060ae739a0..36a5589985 100644 --- a/src/ov_SC07_000/ov_SC07_000_jr_80154C24.c +++ b/src/ov_SC07_000/ov_SC07_000_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC07_000/ov_SC07_000_jr_8015C32C.c b/src/ov_SC07_000/ov_SC07_000_jr_8015C32C.c index 255651af9d..1d118a4734 100644 --- a/src/ov_SC07_000/ov_SC07_000_jr_8015C32C.c +++ b/src/ov_SC07_000/ov_SC07_000_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80185134[]; + extern unsigned short D_80185136[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80185134[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80185136[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80185164; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80185164)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC07_000/ov_SC07_000_jr_8016AB6C.c b/src/ov_SC07_000/ov_SC07_000_jr_8016AB6C.c index ce075fa6af..76d0e9ab36 100644 --- a/src/ov_SC07_000/ov_SC07_000_jr_8016AB6C.c +++ b/src/ov_SC07_000/ov_SC07_000_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_000/nonmatchings/ov_SC07_000_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80185BAC[])(void); diff --git a/src/ov_SC07_001/ov_SC07_001_after.c b/src/ov_SC07_001/ov_SC07_001_after.c index 9943088ada..3e39299db1 100644 --- a/src/ov_SC07_001/ov_SC07_001_after.c +++ b/src/ov_SC07_001/ov_SC07_001_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC07_001/ov_SC07_001_jr_8012ACE0.c b/src/ov_SC07_001/ov_SC07_001_jr_8012ACE0.c index 3eab56b672..644a041aef 100644 --- a/src/ov_SC07_001/ov_SC07_001_jr_8012ACE0.c +++ b/src/ov_SC07_001/ov_SC07_001_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC07_001/ov_SC07_001_jr_80135D20.c b/src/ov_SC07_001/ov_SC07_001_jr_80135D20.c index d533ffa14e..52fa90194c 100644 --- a/src/ov_SC07_001/ov_SC07_001_jr_80135D20.c +++ b/src/ov_SC07_001/ov_SC07_001_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC07_001/ov_SC07_001_jr_80140608.c b/src/ov_SC07_001/ov_SC07_001_jr_80140608.c index 5238144c00..ed403a6bf9 100644 --- a/src/ov_SC07_001/ov_SC07_001_jr_80140608.c +++ b/src/ov_SC07_001/ov_SC07_001_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80182C28[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80182C28[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC07_001/ov_SC07_001_jr_80154C24.c b/src/ov_SC07_001/ov_SC07_001_jr_80154C24.c index 55d4795e19..179cc8dd66 100644 --- a/src/ov_SC07_001/ov_SC07_001_jr_80154C24.c +++ b/src/ov_SC07_001/ov_SC07_001_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC07_001/ov_SC07_001_jr_8015C32C.c b/src/ov_SC07_001/ov_SC07_001_jr_8015C32C.c index 4868b5d057..54f2ea06a7 100644 --- a/src/ov_SC07_001/ov_SC07_001_jr_8015C32C.c +++ b/src/ov_SC07_001/ov_SC07_001_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80183FE4[]; + extern unsigned short D_80183FE6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80183FE4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80183FE6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184014; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184014)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC07_001/ov_SC07_001_jr_8016AB6C.c b/src/ov_SC07_001/ov_SC07_001_jr_8016AB6C.c index 77ded469f5..244b4e9935 100644 --- a/src/ov_SC07_001/ov_SC07_001_jr_8016AB6C.c +++ b/src/ov_SC07_001/ov_SC07_001_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_001/nonmatchings/ov_SC07_001_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80184A5C[])(void); diff --git a/src/ov_SC07_002/ov_SC07_002_after.c b/src/ov_SC07_002/ov_SC07_002_after.c index 1980f4e7df..f34c65e078 100644 --- a/src/ov_SC07_002/ov_SC07_002_after.c +++ b/src/ov_SC07_002/ov_SC07_002_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC07_002/ov_SC07_002_jr_8012ACE0.c b/src/ov_SC07_002/ov_SC07_002_jr_8012ACE0.c index 7e6af30258..bb0dccac8d 100644 --- a/src/ov_SC07_002/ov_SC07_002_jr_8012ACE0.c +++ b/src/ov_SC07_002/ov_SC07_002_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC07_002/ov_SC07_002_jr_80135D20.c b/src/ov_SC07_002/ov_SC07_002_jr_80135D20.c index e3f525fc21..3a1b9d42ad 100644 --- a/src/ov_SC07_002/ov_SC07_002_jr_80135D20.c +++ b/src/ov_SC07_002/ov_SC07_002_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC07_002/ov_SC07_002_jr_80140608.c b/src/ov_SC07_002/ov_SC07_002_jr_80140608.c index 823b4bcdde..5df2c197ec 100644 --- a/src/ov_SC07_002/ov_SC07_002_jr_80140608.c +++ b/src/ov_SC07_002/ov_SC07_002_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_80187EE4[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_80187EE4[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC07_002/ov_SC07_002_jr_80154C24.c b/src/ov_SC07_002/ov_SC07_002_jr_80154C24.c index de27e1149f..170e369141 100644 --- a/src/ov_SC07_002/ov_SC07_002_jr_80154C24.c +++ b/src/ov_SC07_002/ov_SC07_002_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC07_002/ov_SC07_002_jr_8015C32C.c b/src/ov_SC07_002/ov_SC07_002_jr_8015C32C.c index 925e170834..2c8d313810 100644 --- a/src/ov_SC07_002/ov_SC07_002_jr_8015C32C.c +++ b/src/ov_SC07_002/ov_SC07_002_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801892A0[]; + extern unsigned short D_801892A2[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801892A0[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801892A2[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_801892D0; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_801892D0)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC07_002/ov_SC07_002_jr_8016AB6C.c b/src/ov_SC07_002/ov_SC07_002_jr_8016AB6C.c index f434412247..2d5fca01dd 100644 --- a/src/ov_SC07_002/ov_SC07_002_jr_8016AB6C.c +++ b/src/ov_SC07_002/ov_SC07_002_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_002/nonmatchings/ov_SC07_002_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80189D18[])(void); diff --git a/src/ov_SC07_006/ov_SC07_006.c b/src/ov_SC07_006/ov_SC07_006.c index 6cd5db797b..725c801d7e 100644 --- a/src/ov_SC07_006/ov_SC07_006.c +++ b/src/ov_SC07_006/ov_SC07_006.c @@ -1983,7 +1983,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012ef34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012ef70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + DEFINE_func_8012F038() /* dedup: shared engine-core @0x8012f038 (src/shared) */ diff --git a/src/ov_SC07_006/ov_SC07_006_jr_80131340.c b/src/ov_SC07_006/ov_SC07_006_jr_80131340.c index e1af070b53..c95c7a2f91 100644 --- a/src/ov_SC07_006/ov_SC07_006_jr_80131340.c +++ b/src/ov_SC07_006/ov_SC07_006_jr_80131340.c @@ -921,9 +921,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131c78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_80131340", func_80131CA8); -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_80131340", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131d68 (src/shared) */ diff --git a/src/ov_SC07_006/ov_SC07_006_jr_80135D20.c b/src/ov_SC07_006/ov_SC07_006_jr_80135D20.c index 09d82d3494..1ba92bf171 100644 --- a/src/ov_SC07_006/ov_SC07_006_jr_80135D20.c +++ b/src/ov_SC07_006/ov_SC07_006_jr_80135D20.c @@ -1273,7 +1273,69 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372b0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375ec (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767c (src/shared) */ @@ -1281,7 +1343,70 @@ DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767c (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376c8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern s32 func_80139BE0(s32); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + ((void (*)(s32))func_80139BE0)(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377b4 (src/shared) */ diff --git a/src/ov_SC07_006/ov_SC07_006_jr_80140608.c b/src/ov_SC07_006/ov_SC07_006_jr_80140608.c index 46320a463d..7976723fae 100644 --- a/src/ov_SC07_006/ov_SC07_006_jr_80140608.c +++ b/src/ov_SC07_006/ov_SC07_006_jr_80140608.c @@ -206,7 +206,6 @@ extern void func_8012A860(void *a0, int a1); extern void func_8012A8B0(u8 *a0, s32 a1); extern void func_8012A8E8(void); extern u8 D_801202A0[]; -extern u16 D_801270C0; extern void func_8012A988(u8 *a0); extern void func_8012A908(void); extern s32 func_8012ACE0(void *a0); @@ -1740,7 +1739,29 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern u16 D_801270C0; + extern void (*D_8018BB08[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018BB08[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct @@ -5565,7 +5586,80 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014e48c (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014e514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_80140608", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8*); @@ -6280,7 +6374,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126c (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_80140608", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516f0 (src/shared) */ diff --git a/src/ov_SC07_006/ov_SC07_006_jr_80154C24.c b/src/ov_SC07_006/ov_SC07_006_jr_80154C24.c index fd70ffbc84..b06992f7e2 100644 --- a/src/ov_SC07_006/ov_SC07_006_jr_80154C24.c +++ b/src/ov_SC07_006/ov_SC07_006_jr_80154C24.c @@ -2275,7 +2275,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564b0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565c0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC07_006/ov_SC07_006_jr_8015C32C.c b/src/ov_SC07_006/ov_SC07_006_jr_8015C32C.c index b8d2843ef4..4e5d9e6a2c 100644 --- a/src/ov_SC07_006/ov_SC07_006_jr_8015C32C.c +++ b/src/ov_SC07_006/ov_SC07_006_jr_8015C32C.c @@ -4290,7 +4290,58 @@ DEFINE_func_80161240() /* dedup: shared engine-core @0x80161240 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612b8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule // @stuck: none — MATCH @@ -4631,7 +4682,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161b84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161be0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018CEC4[]; + extern unsigned short D_8018CEC6[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018CEC4[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018CEC6[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -4650,7 +4724,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018CEF4; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018CEF4)[param_2]); + } + } +} + // @class: struct @@ -7454,7 +7579,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168ae4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void ((void (*)(void))func_80146C3C)(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(u8*); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + extern void func_800D2318(void); extern void RotMatrixYXZ(void *a0, void *a1); diff --git a/src/ov_SC07_006/ov_SC07_006_jr_8016AE5C.c b/src/ov_SC07_006/ov_SC07_006_jr_8016AE5C.c index 7fb56512f1..a79ef24acd 100644 --- a/src/ov_SC07_006/ov_SC07_006_jr_8016AE5C.c +++ b/src/ov_SC07_006/ov_SC07_006_jr_8016AE5C.c @@ -54,7 +54,6 @@ extern void func_80129C40(s32 a0); extern void func_800D0630(void); extern void func_80145CEC(void); extern void func_80144B9C(); -extern u8 D_800B9A17; extern u8 D_800B9A10; extern void func_80128420(void); extern s32 func_800D0588(void); @@ -1397,7 +1396,6 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); @@ -4685,7 +4683,63 @@ DEFINE_func_8016F094() /* dedup: shared engine-core @0x8016f094 (src/shared) */ INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8016AE5C", func_8016F0AC); -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8016AE5C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016f14c (src/shared) */ @@ -5147,7 +5201,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084c (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8016AE5C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -5212,7 +5287,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170cb0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_006/nonmatchings/ov_SC07_006_jr_8016AE5C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + diff --git a/src/ov_SC07_007/ov_SC07_007.c b/src/ov_SC07_007/ov_SC07_007.c index 9348e95dcf..428fd5658c 100644 --- a/src/ov_SC07_007/ov_SC07_007.c +++ b/src/ov_SC07_007/ov_SC07_007.c @@ -1786,7 +1786,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012ef34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012ef70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + DEFINE_func_8012F038() /* dedup: shared engine-core @0x8012f038 (src/shared) */ diff --git a/src/ov_SC07_007/ov_SC07_007_jr_80131340.c b/src/ov_SC07_007/ov_SC07_007_jr_80131340.c index 83b82374e1..4e154e28fb 100644 --- a/src/ov_SC07_007/ov_SC07_007_jr_80131340.c +++ b/src/ov_SC07_007/ov_SC07_007_jr_80131340.c @@ -921,9 +921,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131c78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_80131340", func_80131CA8); -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_80131340", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131d68 (src/shared) */ diff --git a/src/ov_SC07_007/ov_SC07_007_jr_80135D20.c b/src/ov_SC07_007/ov_SC07_007_jr_80135D20.c index 02d2f2c259..bab851df7f 100644 --- a/src/ov_SC07_007/ov_SC07_007_jr_80135D20.c +++ b/src/ov_SC07_007/ov_SC07_007_jr_80135D20.c @@ -1223,7 +1223,69 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372b0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375ec (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767c (src/shared) */ @@ -1231,7 +1293,70 @@ DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767c (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376c8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern s32 func_80139BE0(s32); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + ((void (*)(s32))func_80139BE0)(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377b4 (src/shared) */ diff --git a/src/ov_SC07_007/ov_SC07_007_jr_80140608.c b/src/ov_SC07_007/ov_SC07_007_jr_80140608.c index 5e118906d0..511baaeaf0 100644 --- a/src/ov_SC07_007/ov_SC07_007_jr_80140608.c +++ b/src/ov_SC07_007/ov_SC07_007_jr_80140608.c @@ -202,8 +202,6 @@ extern int func_8012ACE0(void *a0); extern void func_8012A860(void *a0, int a1); extern void func_8012A8B0(u8 *a0, s32 a1); extern void func_8012A8E8(void); -extern u8 D_801202A0[]; -extern u16 D_801270C0; extern void func_8012A988(u8 *a0); extern void func_8012A908(void); extern s32 func_8012ACE0(void *a0); @@ -1734,7 +1732,29 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern u16 D_801270C0; + extern void (*D_8018462C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8018462C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct @@ -4022,6 +4042,10 @@ extern void func_8012F14C(s32, s32, s32); extern s32 func_80135260(s32 arg0, s32 arg1, s16 *arg2, s16 *arg3); extern void func_8014A1B0(s32 a0, s32 a1); s32 func_8014A048(s32 param_1) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; Loc L; s32 s0; s32 s2; @@ -4135,6 +4159,10 @@ DEFINE_func_8014A71C() /* dedup: shared engine-core @0x8014a71c (src/shared) */ extern s32 func_80029178(s32); s32 func_8014A738(void *arg0) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; typedef struct { s16 vx; s16 vy; @@ -4722,12 +4750,15 @@ extern s32 func_8014EA4C(void *a0, void *a1, void *a2, s32 a3); // Register fallout is natural, no pins needed: a0->$s2, a2->$s3, a1->$s4 (param_3 outranks param_2 // on allocno priority because of its 4 extra uses in the success block). -extern u8 D_801202A0[]; extern u8 D_80126720[]; extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_8014C918(s32 a0, s32 a1); s32 func_8014CF04(s32 a0, s32 a1, void *a2) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; u8 *p; u8 *q; s32 t; @@ -4841,13 +4872,16 @@ s32 func_8014D12C(s32 arg0, void *a1, void *a2) -extern u8 D_801202A0[]; extern u8 D_80126720[]; extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_8014C918(s32 a0, s32 a1); s32 func_8014D2A0(s32 arg0, void *a1, void *a2) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; u16 *arg2 = (u16 *)a2; u8 *p; s32 t; @@ -4912,6 +4946,10 @@ extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_8014C918(s32 a0, s32 a1); s32 func_8014D610(s32 param_1, s32 param_2, u16 *param_3) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; u16 *psVar4; u8 *puVar3; s32 iVar2; @@ -5072,7 +5110,6 @@ extern void func_8012AAAC(void *arg0); extern s16 currentLocationId; -extern u8 D_801202A0[]; extern s32 ratan2(s32 a0, s32 a1); extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_80012A60(s32 a0, s32 a1); @@ -5082,6 +5119,10 @@ extern s32 func_8014DCE0(s32 a0, s32 a1, s32 a2); extern s32 func_80133784(s32 a0, void *a1, s32 a2); s32 func_8014D820(s32 a0, u16 *a1, u16 *a2x) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every LATER function in this TU, which blocks a byte-true decl of a different type. Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ @@ -5383,7 +5424,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014e48c (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014e514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_80140608", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8*); @@ -6044,7 +6159,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126c (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_80140608", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516f0 (src/shared) */ diff --git a/src/ov_SC07_007/ov_SC07_007_jr_80154C24.c b/src/ov_SC07_007/ov_SC07_007_jr_80154C24.c index 9a4545fc16..91e964497e 100644 --- a/src/ov_SC07_007/ov_SC07_007_jr_80154C24.c +++ b/src/ov_SC07_007/ov_SC07_007_jr_80154C24.c @@ -2275,7 +2275,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564b0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565c0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC07_007/ov_SC07_007_jr_8015C32C.c b/src/ov_SC07_007/ov_SC07_007_jr_8015C32C.c index 87fc844ea5..780aff76b5 100644 --- a/src/ov_SC07_007/ov_SC07_007_jr_8015C32C.c +++ b/src/ov_SC07_007/ov_SC07_007_jr_8015C32C.c @@ -4277,7 +4277,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612b8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -4583,7 +4634,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161b84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161be0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_801859E8[]; + extern unsigned short D_801859EA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_801859E8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_801859EA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -4602,7 +4676,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80185A18; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80185A18)[param_2]); + } + } +} + // @class: struct @@ -7392,7 +7517,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168ae4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void ((void (*)(void))func_80146C3C)(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(u8*); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + extern void func_800D2318(void); extern void RotMatrixYXZ(void *a0, void *a1); diff --git a/src/ov_SC07_007/ov_SC07_007_jr_8016AE5C.c b/src/ov_SC07_007/ov_SC07_007_jr_8016AE5C.c index 0fff22fcf9..5327b54f0f 100644 --- a/src/ov_SC07_007/ov_SC07_007_jr_8016AE5C.c +++ b/src/ov_SC07_007/ov_SC07_007_jr_8016AE5C.c @@ -54,7 +54,6 @@ extern void func_80129C40(s32 a0); extern void func_800D0630(void); extern void func_80145CEC(void); extern void func_80144B9C(void); -extern u8 D_800B9A17; extern u8 D_800B9A10; extern void func_80128420(void); extern s32 func_800D0588(void); @@ -1398,7 +1397,6 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); @@ -4673,7 +4671,63 @@ DEFINE_func_8016F094() /* dedup: shared engine-core @0x8016f094 (src/shared) */ INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_8016AE5C", func_8016F0AC); -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_8016AE5C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016f14c (src/shared) */ @@ -5135,7 +5189,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084c (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_8016AE5C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -5200,7 +5275,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170cb0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_007/nonmatchings/ov_SC07_007_jr_8016AE5C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + diff --git a/src/ov_SC07_008/ov_SC07_008_after.c b/src/ov_SC07_008/ov_SC07_008_after.c index 19a5d8f2d1..6c7e8002ac 100644 --- a/src/ov_SC07_008/ov_SC07_008_after.c +++ b/src/ov_SC07_008/ov_SC07_008_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC07_008/ov_SC07_008_jr_8012ACE0.c b/src/ov_SC07_008/ov_SC07_008_jr_8012ACE0.c index 658f11cc54..c3d8ca888e 100644 --- a/src/ov_SC07_008/ov_SC07_008_jr_8012ACE0.c +++ b/src/ov_SC07_008/ov_SC07_008_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC07_008/ov_SC07_008_jr_80135D20.c b/src/ov_SC07_008/ov_SC07_008_jr_80135D20.c index 58d3bec2ea..9157bc8d91 100644 --- a/src/ov_SC07_008/ov_SC07_008_jr_80135D20.c +++ b/src/ov_SC07_008/ov_SC07_008_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC07_008/ov_SC07_008_jr_80140608.c b/src/ov_SC07_008/ov_SC07_008_jr_80140608.c index 35438af73e..642f06c08b 100644 --- a/src/ov_SC07_008/ov_SC07_008_jr_80140608.c +++ b/src/ov_SC07_008/ov_SC07_008_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017E9FC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017E9FC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC07_008/ov_SC07_008_jr_80154C24.c b/src/ov_SC07_008/ov_SC07_008_jr_80154C24.c index 8e58e470e1..7d54d00959 100644 --- a/src/ov_SC07_008/ov_SC07_008_jr_80154C24.c +++ b/src/ov_SC07_008/ov_SC07_008_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC07_008/ov_SC07_008_jr_8015C32C.c b/src/ov_SC07_008/ov_SC07_008_jr_8015C32C.c index 81063bc3e3..1270c6e65d 100644 --- a/src/ov_SC07_008/ov_SC07_008_jr_8015C32C.c +++ b/src/ov_SC07_008/ov_SC07_008_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8017FDB8[]; + extern unsigned short D_8017FDBA[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8017FDB8[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8017FDBA[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8017FDE8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8017FDE8)[param_2]); + } + } +} + // @class: struct @@ -5928,7 +6053,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC07_008/ov_SC07_008_jr_8016AB6C.c b/src/ov_SC07_008/ov_SC07_008_jr_8016AB6C.c index 9b028ee8a2..11835259b5 100644 --- a/src/ov_SC07_008/ov_SC07_008_jr_8016AB6C.c +++ b/src/ov_SC07_008/ov_SC07_008_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3881,7 +3879,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4244,7 +4298,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4294,7 +4369,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_008/nonmatchings/ov_SC07_008_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_80180830[])(void); diff --git a/src/ov_SC07_009/ov_SC07_009_after.c b/src/ov_SC07_009/ov_SC07_009_after.c index d7859fd506..785b80a617 100644 --- a/src/ov_SC07_009/ov_SC07_009_after.c +++ b/src/ov_SC07_009/ov_SC07_009_after.c @@ -2281,7 +2281,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014E48C (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014E514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_after", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8 *a0); @@ -2996,7 +3070,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126C (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_after", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516F0 (src/shared) */ diff --git a/src/ov_SC07_009/ov_SC07_009_jr_8012ACE0.c b/src/ov_SC07_009/ov_SC07_009_jr_8012ACE0.c index 9de99d8ffa..1f6e622196 100644 --- a/src/ov_SC07_009/ov_SC07_009_jr_8012ACE0.c +++ b/src/ov_SC07_009/ov_SC07_009_jr_8012ACE0.c @@ -988,7 +988,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012EF34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012EF70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_8012ACE0", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + // @class: other @@ -1898,9 +2033,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131C78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_8012ACE0", func_80131CA8); -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_8012ACE0", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131D68 (src/shared) */ diff --git a/src/ov_SC07_009/ov_SC07_009_jr_80135D20.c b/src/ov_SC07_009/ov_SC07_009_jr_80135D20.c index 9fded23f1e..6203022f00 100644 --- a/src/ov_SC07_009/ov_SC07_009_jr_80135D20.c +++ b/src/ov_SC07_009/ov_SC07_009_jr_80135D20.c @@ -1287,13 +1287,138 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372B0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375EC (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767C (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376C8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern void func_80139BE0(s32 a0); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + func_80139BE0(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377B4 (src/shared) */ diff --git a/src/ov_SC07_009/ov_SC07_009_jr_80140608.c b/src/ov_SC07_009/ov_SC07_009_jr_80140608.c index 6b83a47b4a..e6fd9cd40b 100644 --- a/src/ov_SC07_009/ov_SC07_009_jr_80140608.c +++ b/src/ov_SC07_009/ov_SC07_009_jr_80140608.c @@ -1809,7 +1809,28 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern void (*D_8017F4A0[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017F4A0[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct diff --git a/src/ov_SC07_009/ov_SC07_009_jr_80154C24.c b/src/ov_SC07_009/ov_SC07_009_jr_80154C24.c index 180381c410..94193d05d0 100644 --- a/src/ov_SC07_009/ov_SC07_009_jr_80154C24.c +++ b/src/ov_SC07_009/ov_SC07_009_jr_80154C24.c @@ -1358,7 +1358,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564B0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565C0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC07_009/ov_SC07_009_jr_8015C32C.c b/src/ov_SC07_009/ov_SC07_009_jr_8015C32C.c index 2231118db1..4d6a2379e0 100644 --- a/src/ov_SC07_009/ov_SC07_009_jr_8015C32C.c +++ b/src/ov_SC07_009/ov_SC07_009_jr_8015C32C.c @@ -3288,7 +3288,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612B8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -3574,7 +3625,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161B84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161BE0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_8018085C[]; + extern unsigned short D_8018085E[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_8018085C[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018085E[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -3592,7 +3666,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_8018088C; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_8018088C)[param_2]); + } + } +} + // @class: struct @@ -5927,7 +6052,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168AE4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void func_80146C3C(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(void); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168BDC (src/shared) */ diff --git a/src/ov_SC07_009/ov_SC07_009_jr_8016AB6C.c b/src/ov_SC07_009/ov_SC07_009_jr_8016AB6C.c index 0109318492..1b496a4e35 100644 --- a/src/ov_SC07_009/ov_SC07_009_jr_8016AB6C.c +++ b/src/ov_SC07_009/ov_SC07_009_jr_8016AB6C.c @@ -469,12 +469,10 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BB24(s32 a0, s32 a1, s32 a2); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -3882,7 +3880,63 @@ s32 func_8016F0AC() -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_8016AB6C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016F14C (src/shared) */ @@ -4245,7 +4299,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084C (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_8016AB6C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4295,7 +4370,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170CB0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_009/nonmatchings/ov_SC07_009_jr_8016AB6C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + extern void (*D_801812D4[])(void); diff --git a/src/ov_SC07_010/ov_SC07_010.c b/src/ov_SC07_010/ov_SC07_010.c index 6bb8443907..de6a4e42db 100644 --- a/src/ov_SC07_010/ov_SC07_010.c +++ b/src/ov_SC07_010/ov_SC07_010.c @@ -1786,7 +1786,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012ef34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012ef70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + DEFINE_func_8012F038() /* dedup: shared engine-core @0x8012f038 (src/shared) */ diff --git a/src/ov_SC07_010/ov_SC07_010_jr_80131340.c b/src/ov_SC07_010/ov_SC07_010_jr_80131340.c index d275d3a1ad..1a8c3d4d95 100644 --- a/src/ov_SC07_010/ov_SC07_010_jr_80131340.c +++ b/src/ov_SC07_010/ov_SC07_010_jr_80131340.c @@ -926,9 +926,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131c78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_80131340", func_80131CA8); -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_80131340", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131d68 (src/shared) */ diff --git a/src/ov_SC07_010/ov_SC07_010_jr_80135D20.c b/src/ov_SC07_010/ov_SC07_010_jr_80135D20.c index d221a21a25..6704dddf51 100644 --- a/src/ov_SC07_010/ov_SC07_010_jr_80135D20.c +++ b/src/ov_SC07_010/ov_SC07_010_jr_80135D20.c @@ -1228,7 +1228,69 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372b0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375ec (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767c (src/shared) */ @@ -1236,7 +1298,70 @@ DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767c (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376c8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern s32 func_80139BE0(s32); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + ((void (*)(s32))func_80139BE0)(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377b4 (src/shared) */ diff --git a/src/ov_SC07_010/ov_SC07_010_jr_80140608.c b/src/ov_SC07_010/ov_SC07_010_jr_80140608.c index 3037bbc6e7..b20bbd9f45 100644 --- a/src/ov_SC07_010/ov_SC07_010_jr_80140608.c +++ b/src/ov_SC07_010/ov_SC07_010_jr_80140608.c @@ -50,7 +50,6 @@ extern s32 func_8013E410(void); extern s32 func_8013E448(s32 a0); extern void func_8012C724(s32 a0, s32 a1); extern void func_8013E370(void); -extern u8 D_801202A0[]; extern s32 (*D_801274D0)(s32); extern s32 D_801274D8; extern s32 D_801274DC; @@ -1090,7 +1089,29 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern u16 D_801270C0; + extern void (*D_801836BC[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_801836BC[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct @@ -3179,6 +3200,10 @@ DEFINE_func_8014A71C() /* dedup: shared engine-core @0x8014a71c (src/shared) */ extern s32 func_80029178(s32); s32 func_8014A738(void *arg0) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; typedef struct { s16 vx; s16 vy; @@ -3767,11 +3792,14 @@ extern s32 func_8014EA4C(void *a0, void *a1, void *a2, s32 a3); // Register fallout is natural, no pins needed: a0->$s2, a2->$s3, a1->$s4 (param_3 outranks param_2 // on allocno priority because of its 4 extra uses in the success block). -extern u8 D_801202A0[]; extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_8014C918(s32 a0, s32 a1); s32 func_8014CF04(s32 a0, s32 a1, void *a2) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; extern u8 D_80126720[]; u8 *p; @@ -3887,12 +3915,15 @@ s32 func_8014D12C(s32 arg0, void *a1, void *a2) -extern u8 D_801202A0[]; extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_8014C918(s32 a0, s32 a1); s32 func_8014D2A0(s32 arg0, void *a1, void *a2) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; extern u8 D_80126720[]; u16 *arg2 = (u16 *)a2; @@ -3959,6 +3990,10 @@ extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_8014C918(s32 a0, s32 a1); s32 func_8014D610(s32 param_1, s32 param_2, u16 *param_3) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; u16 *psVar4; u8 *puVar3; s32 iVar2; @@ -4119,7 +4154,6 @@ extern void func_8012AAAC(void *arg0); extern s16 currentLocationId; -extern u8 D_801202A0[]; extern s32 ratan2(s32 a0, s32 a1); extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_80012A60(s32 a0, s32 a1); @@ -4129,6 +4163,10 @@ extern s32 func_8014DCE0(s32 a0, s32 a1, s32 a2); extern s32 func_80133784(s32 a0, void *a1, s32 a2); s32 func_8014D820(s32 a0, u16 *a1, u16 *a2x) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; extern u8 D_80183C10[]; extern u8 D_801152A8[]; @@ -4427,7 +4465,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014e48c (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014e514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_80140608", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8*); @@ -5092,7 +5204,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126c (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_80140608", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516f0 (src/shared) */ diff --git a/src/ov_SC07_010/ov_SC07_010_jr_80154C24.c b/src/ov_SC07_010/ov_SC07_010_jr_80154C24.c index 063b8bb507..f781588a3a 100644 --- a/src/ov_SC07_010/ov_SC07_010_jr_80154C24.c +++ b/src/ov_SC07_010/ov_SC07_010_jr_80154C24.c @@ -1696,7 +1696,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564b0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565c0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC07_010/ov_SC07_010_jr_8015C32C.c b/src/ov_SC07_010/ov_SC07_010_jr_8015C32C.c index afa833356a..4a733ab6df 100644 --- a/src/ov_SC07_010/ov_SC07_010_jr_8015C32C.c +++ b/src/ov_SC07_010/ov_SC07_010_jr_8015C32C.c @@ -3704,7 +3704,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612b8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -4010,7 +4061,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161b84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161be0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80184A78[]; + extern unsigned short D_80184A7A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80184A78[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_80184A7A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -4029,7 +4103,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80184AA8; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80184AA8)[param_2]); + } + } +} + // @class: struct @@ -6799,7 +6924,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168ae4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void ((void (*)(void))func_80146C3C)(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(u8*); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + DEFINE_func_80168BDC() /* dedup: shared engine-core @0x80168bdc (src/shared) */ diff --git a/src/ov_SC07_010/ov_SC07_010_jr_8016AE5C.c b/src/ov_SC07_010/ov_SC07_010_jr_8016AE5C.c index 8f6b09ba72..9b017bca1b 100644 --- a/src/ov_SC07_010/ov_SC07_010_jr_8016AE5C.c +++ b/src/ov_SC07_010/ov_SC07_010_jr_8016AE5C.c @@ -809,11 +809,9 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); -extern u8 D_800B9A17; extern void func_8014BCC0(s32 a0, s32 a1); extern u16 D_80078EB6; extern s32 func_8014BCEC(s32 a0, s32 a1); @@ -4122,7 +4120,63 @@ DEFINE_func_8016F094() /* dedup: shared engine-core @0x8016f094 (src/shared) */ INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_8016AE5C", func_8016F0AC); -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_8016AE5C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016f14c (src/shared) */ @@ -4584,7 +4638,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084c (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_8016AE5C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -4649,7 +4724,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170cb0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_010/nonmatchings/ov_SC07_010_jr_8016AE5C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} + diff --git a/src/ov_SC07_011/ov_SC07_011.c b/src/ov_SC07_011/ov_SC07_011.c index f183633719..843f15b4b8 100644 --- a/src/ov_SC07_011/ov_SC07_011.c +++ b/src/ov_SC07_011/ov_SC07_011.c @@ -1786,7 +1786,142 @@ DEFINE_func_8012EF34() /* dedup: shared engine-core @0x8012ef34 (src/shared) */ DEFINE_func_8012EF70() /* dedup: shared engine-core @0x8012ef70 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011", func_8012EFB8); +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_rtps() __asm__ volatile ("nop;nop;rtps") +#define gte_stsxy(r0) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) +#define gte_SetRotMatrix(r0) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_SetTransMatrix(r0) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) +#define gte_ldv0(r0) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) +#define gte_stflg(r0) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void + * func_8012EFB8(s32 a0);` inside the DEFINE_func_* macros in + * src/shared/engine_core.h (the one at src/ov_SC01_077/ov_SC01_077_jr_8012ACE0.c:1169 + * comes from DEFINE_func_8012EF70()). The byte-true body returns s32 and takes + * TWO pointers, so it conflicts on BOTH the return axis and the arity axis. + * Aliasing the C name sidesteps both with no header edit and no codegen change. + * This declaration MUST travel with the body. */ +s32 aF8012EFB8(void *param_1, void *param_2) __asm__("func_8012EFB8"); + +s32 aF8012EFB8(param_1, param_2) + void *param_1; + void *param_2; +{ + extern u8 D_800AF648; + s32 *m; + s32 flag; + + m = (s32 *)&D_800AF648; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(param_1); + gte_rtps(); + gte_stsxy(param_2); + gte_stflg(&flag); + return flag; +} + DEFINE_func_8012F038() /* dedup: shared engine-core @0x8012f038 (src/shared) */ diff --git a/src/ov_SC07_011/ov_SC07_011_jr_80131340.c b/src/ov_SC07_011/ov_SC07_011_jr_80131340.c index aa168b7258..25b5ce7d30 100644 --- a/src/ov_SC07_011/ov_SC07_011_jr_80131340.c +++ b/src/ov_SC07_011/ov_SC07_011_jr_80131340.c @@ -921,9 +921,105 @@ void func_80131B14() { DEFINE_func_80131C78() /* dedup: shared engine-core @0x80131c78 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_80131340", func_80131CA8); -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_80131340", func_80131CF4); +/* func_80131CA8 — RECONCILE (§37/§124 asm-label alias) + * + * Declaration-environment conflict, NOT a body problem: + * The TU's DEFINE_func_80131C78() expansion (jr_8012ACE0.c L2232) and the later + * DEFINE_func_80131D68() expansion (L2250) each emit the canonical + * extern void func_80131CA8(int a0, int a1); + * (src/shared/engine_core.h, e.g. L1961/L4131). The byte-correct body is + * `s32 f(int)` — it disagrees on BOTH the return axis (s32 vs void) and the + * arity axis (1 vs 2), so it collides with the macro-emitted declaration on + * either side of the slot. Escape 1 (conform + cast at use) cannot apply: the + * conflict is on the DEFINED symbol itself. + * + * Fix: define under the C name aF80131CA8 with __asm__("func_80131CA8") so the + * C-level name never meets the canonical declaration, while the emitted symbol + * is unchanged. Codegen-neutral — match_one still prints MATCH (19/19). + * This declaration MUST travel with the body. + * + * func_80131CF4 is declared here 1-arg s32, matching the engine_core canonical + * at engine_core.h L6563; it does not collide anywhere in this TU (verified by + * a full preprocess+cc1 of the spliced TU: diagnostic-free apart from + * pre-existing warnings). + */ +extern s32 func_80131CF4(s32 a0); + +s32 aF80131CA8(int a0) __asm__("func_80131CA8"); + +s32 aF80131CA8(int a0) +{ + s32 (*fp)(int) = (s32 (*)(int))func_80131CF4(*(s32 *)((u8 *)a0 + 0xBC)); + if (fp != 0) { + fp(a0); + return 1; + } + return 0; +} + + + + +/* func_80131CF4 — table lookup: walk (key, value) pairs; key 0xD chains to the + * next table via value; key == wanted returns value; key 0 terminates. + * + * The __asm__ inside the body is a matching pin, not logic. It does two things + * gcc-2.7.2 -O2 needs in order to reproduce the target's preheader exactly: + * 1. "0"(0xD) forces the 0xD compare constant to be materialised HERE + * (`addiu $v1,$zero,0xD`) instead of being loop-hoisted, which places it + * AFTER the preheader load and drops the load-delay nop at the loop head. + * 2. the "memory" clobber stops CSE folding the preheader re-read of a0[0] + * into the guard's read (that CSE costs the second `lw $v0,0x0($a0)`). + * The asm body is empty, so it emits no instruction of its own. + * + * RECONCILE (§37/§124 asm-label alias) — the gate error was + * jr_8012ACE0.c:2248 conflicting types for func_80131CF4 || :2164 previous declaration + * :2164 is `DEFINE_func_80131AC8()`, whose expansion (src/shared/engine_core.h:6563) + * carries the STALE 1-arg engine-core canonical `extern s32 func_80131CF4(s32 a0);`. + * That prototype is wrong for this function (the TU's own call sites at L1780/L1903/ + * L2147 all declare and call the real 2-arg form `int func_80131CF4(int, int)`), but + * it is file-visible before the splice point, so a file-scope definition with the true + * signature is a hard `conflicting types` (arity AND param type). Conforming the + * definition to the 1-arg prototype is impossible without changing codegen, and the + * §42 `register s32 a1 __asm__("$5")` route would still leave the param-type mismatch + * on a0 (s32 vs s32*). So the definition is given a different C identifier and pinned + * to the real symbol with an asm label: no declaration ever collides, the emitted + * label is still `func_80131CF4`, and the callers' `jal func_80131CF4` binds to it. + * Zero header edits; body byte-identical (match_one: MATCH, 29 ins). + */ +s32 aF80131CF4(s32 *a0, s32 a1) __asm__("func_80131CF4"); + +s32 aF80131CF4(s32 *a0, s32 a1) { + s32 v0; + s32 k; + + if (a0 == 0) { + return 0; + } + goto enter; +found: + return a0[1]; +enter: + if (a0[0] == 0) { + return 0; + } + __asm__ __volatile__("" : "=r"(k) : "0"(0xD) : "memory"); + v0 = a0[0]; + do { + if (v0 == k) { + a0 = (s32 *)a0[1]; + } else { + if (v0 == a1) { + goto found; + } + a0 = (s32 *)((u8 *)a0 + 8); + } + v0 = a0[0]; + } while (v0 != 0); + return 0; +} + DEFINE_func_80131D68() /* dedup: shared engine-core @0x80131d68 (src/shared) */ diff --git a/src/ov_SC07_011/ov_SC07_011_jr_80135D20.c b/src/ov_SC07_011/ov_SC07_011_jr_80135D20.c index 65d20cf329..d1bc45aeb1 100644 --- a/src/ov_SC07_011/ov_SC07_011_jr_80135D20.c +++ b/src/ov_SC07_011/ov_SC07_011_jr_80135D20.c @@ -1223,7 +1223,69 @@ DEFINE_func_801372B0() /* dedup: shared engine-core @0x801372b0 (src/shared) */ DEFINE_func_801375EC() /* dedup: shared engine-core @0x801375ec (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_80135D20", func_80137614); +extern void func_801375EC(s32 a0, s16 a1); + +// @class: schedule (arg-copy placement) — PIN-FREE +// match_one: MATCH (26 ins). rtu: MATCH (26 ins) with NO //@EDIT and NO header edit. +// +// ---- RECONCILE 2026-08-01 (uc2 -> uc3) --------------------------------------------------- +// GATE ERROR was: +// jr_80135D20.c:1412 conflicting types for func_80137614 || :1362 previous declaration +// :1362 is `DEFINE_func_801375EC()`, whose expansion (src/shared/engine_core.h:2819) carries +// extern void func_80137614(s32 a0, s32 a1, s32 a2); +// and it is instantiated two lines ABOVE this function's INCLUDE_ASM splice point (:1364). +// :1412 is this draft's own definition. So the conflicting symbol is func_80137614 ITSELF +// and the axis is the §73 RETURN type: the shared header says `void`, the target ends +// `addu $v0, $s0, $zero` and callers in other overlays consume the result +// (ov_SC03_099_jr_80178D40.c already declares it `extern s32 func_80137614(s32,s32,s32)`), +// so the definition MUST stay s32. Re-voiding the definition is the decay loop: gcc-2.7.2 +// silently drops the $v0 set and you get 25/26 ins, class LENGTH-DRIFT/-1. +// +// ESCAPE USED: §37/§124 ASM-LABEL ALIAS (escape #2). The definition is given a distinct C +// identifier `aF80137614` with __asm__("func_80137614"), so it never collides with the +// header's `extern void func_80137614` while still emitting the canonical symbol. Zero +// header edits, zero TU edits, blast radius 0 — this replaces uc2's //@EDIT §65b de-macroize +// of DEFINE_func_801375EC, which would have had to be repeated at all 138 sweep sites. +// The TU already uses this exact idiom 60-odd lines below +// (`void *aF801376E8(int a0, int a1) __asm__("func_801376E8");`), so it travels to siblings. +// The in-TU call from func_801375EC still binds to the header's extern declaration and +// resolves to the same symbol at link time; func_801375EC's own 10 instructions are unchanged. +// +// ---- CODEGEN (unchanged from uc2 — do not touch) ----------------------------------------- +// Residual before the lever: 4 mismatched, the (sw $sN / addu $sN,$aX) prologue PAIRS mirrored — +// target births $s2<-a2 then $s3<-a1; the naive draft births $s3<-a1 then $s2<-a2 (parm order), +// and drags `sw $s3` above `sw $s2` with it. Register ASSIGNMENT was already right in both, so +// this is NOT a regalloc problem. Measured INERT: body statement order, extra plain locals, +// K&R parm-declaration order (incl. declaring a2 before a1 in the K&R decl block). +// +// LEVER — cookbook §67 (arg-copy PLACEMENT). One zero-instruction launder of the a1 parameter +// pins WHERE its entry copy materialises; a2's copy then takes the earlier slot and the register +// saves fall back into ascending order. DIRECTION MATTERS and is the opposite of the intuition: +// laundering a2 (the copy that must move EARLIER) is inert — launder the copy that must land +// LATER. §67 rule 1 respected: the laundered local is NOT pinned. A pinned variant +// (register s32 __asm__("$18") / __asm__("$19") on a2/a1) also MATCHes, but pins are strictly +// worse for the ×138 family sweep (§42e/§86 pin guard), so the pin-free single launder is banked. +// +// Shape copied from the matched next-door sibling DEFINE_func_8013767C() (engine_core.h), +// which is this function minus the 0xD8 mask and the +0x1C halfword store. + +extern s32 func_801399A8(void); +extern void func_801377B4(s32 a0, s32 a1, s32 a2); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) __asm__("func_80137614"); + +s32 aF80137614(s32 a0, s32 a1, s32 a2) +{ + s32 v1, s0; + __asm__("" : "=r"(v1) : "0"(a1)); + s0 = func_801399A8(); + if (s0 != 0) { + func_801377B4(a0, a2 & 0xD8, s0); + *(s16 *)(s0 + 0x1C) = v1; + } + return s0; +} + DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767c (src/shared) */ @@ -1231,7 +1293,70 @@ DEFINE_func_8013767C() /* dedup: shared engine-core @0x8013767c (src/shared) */ DEFINE_func_801376C8() /* dedup: shared engine-core @0x801376c8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_80135D20", func_801376E8); + +/* @class: plumbing (globals-around-two-calls) + * @stuck: none — MATCH (51 ins). + * + * RECONCILE (§37/§124 ASM-LABEL ALIAS) — no TU / header edit required. + * Gate error: ov_SC01_077_jr_80135D20.c:1415: conflicting types for `func_801376E8' + * || previous declaration at :1368 + * :1368 is DEFINE_func_801376C8(), the shared engine-core macro that carries + * `extern void func_801376E8(int a0, int a1);` — a VOID return. The target's last + * pre-epilogue instruction is `addu $v0, $s1, $zero`, i.e. it RETURNS &D_801269F0, + * so the definition MUST be non-void; a `void` definition makes gcc-2.7.2 merely warn + * and DROP the returned value, killing that instruction (LENGTH-DRIFT/-1, 50 ins). + * The conflict is on func_801376E8 ITSELF and is a pure RETURN-type disagreement + * (params already agree: int,int), so the §37/§124 alias applies verbatim: + * define under the C name aF801376E8 with __asm__("func_801376E8") so the C-level + * declaration never collides, while the emitted symbol is still func_801376E8. + * Codegen is untouched — an asm label renames the symbol, nothing else. + * (The earlier draft's `//@EDIT` widening of the shared extern to `void *` is a + * T2 fleet-shared edit; the alias is T0 draft-only. Cookbook §124: fix the reader, + * not the source.) + * + * Byte levers (unchanged from the passing draft): + * - `obj` local holding (s32)&D_801269F0: the address is CSEd ONCE into $s1 and reused + * for both jal args and the return (same idiom as the neighbouring func_80137B80). + * - D_80126A24 is s16 (`lh`) but D_80126A26 is u16 (`lhu`) with an explicit (s16) cast + * at the use site: gcc combines the cast's `sll 16; sra 16` with the `>>1` into + * `sll 16; sra 17`, which is exactly what the target emits. A plain s16 D_80126A26 + * would give `lh; sra 1` instead. + * - D_80126A14 as u16[2] (`lhu` loads, `%lo(D_80126A14 + 0x2)` for element 1). + */ + + +extern void func_801377B4(s32 a0, s32 a1, s32 a2); +extern s32 func_80139BE0(s32); + +void *aF801376E8(int a0, int a1) __asm__("func_801376E8"); + +void *aF801376E8(int a0, int a1) +{ + + extern s32 D_801269F0; + extern u8 D_801269FD; + extern u16 D_80126A14[]; + extern s16 D_80126A1C; + extern s16 D_80126A20; + extern s16 D_80126A22; + extern s16 D_80126A24; + extern u16 D_80126A26; + u16 *p = (u16 *)a1; + s32 obj = (s32)&D_801269F0; + + func_801377B4(a0, 0x6200, obj); + D_80126A1C = 0x1C; + D_801269FD = 0; + if (p != 0) { + D_80126A14[0] = p[0]; + D_80126A14[1] = p[1]; + } + ((void (*)(s32))func_80139BE0)(obj); + D_80126A20 = (D_80126A14[0] + 0x28) - ((D_80126A24 + 0x28) >> 1); + D_80126A22 = D_80126A14[1] - ((s16)D_80126A26 >> 1); + return (void *)obj; +} + DEFINE_func_801377B4() /* dedup: shared engine-core @0x801377b4 (src/shared) */ diff --git a/src/ov_SC07_011/ov_SC07_011_jr_80140608.c b/src/ov_SC07_011/ov_SC07_011_jr_80140608.c index 5a66aa1956..5d3648f6fb 100644 --- a/src/ov_SC07_011/ov_SC07_011_jr_80140608.c +++ b/src/ov_SC07_011/ov_SC07_011_jr_80140608.c @@ -202,8 +202,6 @@ extern int func_8012ACE0(void *a0); extern void func_8012A860(void *a0, int a1); extern void func_8012A8B0(u8 *a0, s32 a1); extern void func_8012A8E8(void); -extern u8 D_801202A0[]; -extern u16 D_801270C0; extern void func_8012A988(u8 *a0); extern void func_8012A908(void); extern s32 func_8012ACE0(void *a0); @@ -1736,7 +1734,29 @@ void func_801429C4(int param_1) } -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_80140608", func_80142A10); + +// @class: struct +// @stuck: none — MATCH (array-of-fnptr %lo-fold + signed-halfword guard) +// @reconcile: func_80142A80 is DEFINED later in this TU as `void func_80142A80(void)`. +// The uc2 draft declared it `extern void func_80142A80(int)` -> conflicting types. +// Escape §17a-1/§20: conform the declaration to the TU's, cast at the use site +// (codegen-neutral: the argument still lands in $a0). + +extern void func_80142A80(void); + +void func_80142A10(int param_1) +{ + + extern u16 D_801270C0; + extern void (*D_8017FD7C[])(); + if (*(s16 *)&D_801270C0 != 3) { + D_8017FD7C[*(u16 *)(param_1 + 2)](); + if (*(s16 *)(param_1 + 0xFC) != 0) { + ((void (*)(int))func_80142A80)(param_1); + } + } +} + // @class: struct @@ -4024,6 +4044,10 @@ extern void func_8012F14C(s32, s32, s32); extern s32 func_80135260(s32 arg0, s32 arg1, s16 *arg2, s16 *arg3); extern void func_8014A1B0(s32 a0, s32 a1); s32 func_8014A048(s32 param_1) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; Loc L; s32 s0; s32 s2; @@ -4137,6 +4161,10 @@ DEFINE_func_8014A71C() /* dedup: shared engine-core @0x8014a71c (src/shared) */ extern s32 func_80029178(s32); s32 func_8014A738(void *arg0) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; typedef struct { s16 vx; s16 vy; @@ -4724,12 +4752,15 @@ extern s32 func_8014EA4C(void *a0, void *a1, void *a2, s32 a3); // Register fallout is natural, no pins needed: a0->$s2, a2->$s3, a1->$s4 (param_3 outranks param_2 // on allocno priority because of its 4 extra uses in the success block). -extern u8 D_801202A0[]; extern u8 D_80126720[]; extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_8014C918(s32 a0, s32 a1); s32 func_8014CF04(s32 a0, s32 a1, void *a2) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; u8 *p; u8 *q; s32 t; @@ -4843,13 +4874,16 @@ s32 func_8014D12C(s32 arg0, void *a1, void *a2) -extern u8 D_801202A0[]; extern u8 D_80126720[]; extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_8014C918(s32 a0, s32 a1); s32 func_8014D2A0(s32 arg0, void *a1, void *a2) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; u16 *arg2 = (u16 *)a2; u8 *p; s32 t; @@ -4914,6 +4948,10 @@ extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_8014C918(s32 a0, s32 a1); s32 func_8014D610(s32 param_1, s32 param_2, u16 *param_3) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; u16 *psVar4; u8 *puVar3; s32 iVar2; @@ -5074,7 +5112,6 @@ extern void func_8012AAAC(void *arg0); extern s16 currentLocationId; -extern u8 D_801202A0[]; extern s32 ratan2(s32 a0, s32 a1); extern s32 func_80135A4C(s32 a0, s32 a1, s32 *a2, s32 a3); extern s32 func_80012A60(s32 a0, s32 a1); @@ -5084,6 +5121,10 @@ extern s32 func_8014DCE0(s32 a0, s32 a1, s32 a2); extern s32 func_80133784(s32 a0, void *a1, s32 a2); s32 func_8014D820(s32 a0, u16 *a1, u16 *a2x) { + /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every + LATER function in this TU, which blocks a byte-true decl of a different type. + Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ + extern u8 D_801202A0[]; /* [T51] scoped in from file scope: a file-scope decl of these symbols constrains every LATER function in this TU, which blocks a byte-true decl of a different type. Declaration-only move (cookbook §103); the whole-binary byte-gate is the arbiter. */ @@ -5385,7 +5426,81 @@ DEFINE_func_8014E48C() /* dedup: shared engine-core @0x8014e48c (src/shared) */ DEFINE_func_8014E514() /* dedup: shared engine-core @0x8014e514 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_80140608", func_8014E5B4); +// — the guard makes $v1, CSE turns the in-body `e = p + 0x6480` into a copy of it, and +// regalloc does not coalesce (gcc-2.7.2 has no coalescing, regalloc.md K8). The back edge +// then uses the register ($s5), NOT a rematerialised %hi/%lo(D_80126720) as in +// func_8014CF04 — so do NOT reuse that sibling's absolute-bound half here. +// +// (2) TWO INDUCTION VARIABLES, secondary base +0xE. `q = p + 0xE` carries every access except +// the two that use the entry pointer itself (`*(u16 *)p` and `*(u8 **)(a0+0x17C) = p`): +// offsets -0x8 / 0x0 / 0x12 / 0x4A / 0x4E off q == 0x6 / 0xE / 0x20 / 0x58 / 0x5C off p. +// A single-pointer spelling is NOT equivalent: gcc emits ONE base and the function comes +// out 56 ins (verified). The negative displacement is the tell that the second pointer is +// in the source, not a compiler-made giv. +// +// (3) REGISTER PINS ($16/$17) — the one non-obvious lever, and it is a genuine density +// knife-edge (regalloc.md K2 / §RC-15). Unpinned, the .lreg reads +// p: 9 refs / 24 insns -> floor_log2(9)*9/24 = 1.125 +// q: 8 refs / 22 insns -> floor_log2(8)*8/22 = 1.091 +// so p wins $s0 by ~3% and the whole function comes out with p/q SWAPPED (17 mismatches, +// every one of them a bare $s0<->$s1 rename). The target wants q in $s0. Decl-order swap +// does nothing (ties are not what decides it — p genuinely out-scores q). Rather than +// hunt a +1-ref anchor (§RC-11 `#APP` would sit next to three delay slots that maspsx +// fills: the `beqz $a1` slot, the `jal` slot and the `bnez` slot), pin both: +// register u8 *p __asm__("$17"); register u8 *q __asm__("$16"); +// Both are callee-saved, so the live-across-`jal` range is §42e/§74-safe by construction +// (the ov_SC03_099 func_8014D820 caveat only applies to caller-saved pins). Everything +// else falls out naturally: a0->$s2, a1->$s3, a2->$s4, e->$s5. +// +// (4) `t = *(s32 *)(q + 0x4A)` is loaded ONCE and reused as arg1 of func_80135888 — that is +// why `lw $a1, 0x4A($s0)` sits at the zero-test rather than at the call. +// +// The func_80135888 decl below is byte-compatible with the one this TU already carries at +// ov_SC01_077_after.c:2303, so it is not a second conflict axis. + +extern s32 func_80135888(s32 a0, s32 a1, s32 a2, s32 a3); + +/* §37/§124 asm-label alias: the fleet canon declares this `extern void` (engine_core.h:13392, + * inside DEFINE_func_8014E48C()); the byte-true body must return s32. Aliasing the C name + * sidesteps the RETURN-axis conflict with no header edit. This line MUST travel with the body. */ +s32 aF8014E5B4(s32 a0, void *a1, void *a2) __asm__("func_8014E5B4"); + +s32 aF8014E5B4(s32 a0, void *a1, void *a2) +{ + + extern u8 D_801202A0[]; + register u8 *p __asm__("$17"); + register u8 *q __asm__("$16"); + u8 *e; + s32 t; + + p = D_801202A0; + if (p < p + 0x6480) { + e = p + 0x6480; + q = p + 0xE; + loop: + if (*(u16 *)p != 0) { + if ((*(u16 *)(q + 0x4E) & 0x40) != 0) { + t = *(s32 *)(q + 0x4A); + if (t != 0) { + if (func_80135888(*(s32 *)(q + 0x12), t, (s32)a1, (s32)a2) != 0) { + *(u8 **)(a0 + 0x17C) = p; + *(u16 *)(a0 + 6) = *(u16 *)(q - 8); + *(u16 *)(a0 + 0xE) = *(u16 *)q; + return 1; + } + } + } + } + p += 0x10C; + q += 0x10C; + if (p < e) { + goto loop; + } + } + return 0; +} + extern void func_8014E6F8(struct SubE6F8*); @@ -6046,7 +6161,49 @@ DEFINE_func_80151238() /* dedup: shared engine-core @0x80151238 (src/shared) */ DEFINE_func_8015126C() /* dedup: shared engine-core @0x8015126c (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_80140608", func_80151664); + +/* func_80151664 — INTEGRATE lane. + * Fleet canon (src/shared/engine_core.h:2924, inside DEFINE_func_8015410C()) declares + * extern void func_80151664(void); + * so a `void func_80151664(s32 a0)` definition is `conflicting types` in every TU that + * expands that macro (ov_SC01_077_after.c:3910 does). Cookbook §73 PARAMS axis / §42: + * keep the canonical (void) signature and read the incoming argument through a $a0 pin. + * The decl below is reproduced verbatim so this file compiles the real conflict. */ +extern void func_80151664(void); + +extern void func_80154A74(s32 a0, s32 a1); +extern void func_801553C0(s32 a0); +extern void func_801470AC(s32 *a0); +extern void func_801472B4(void *a0); +extern void func_801477E8(s32 *a0, s32 a1); +extern void func_80153C18(); /* fleet canon: K&R empty prototype (engine_core.h:75, :1682 defines it (void)); + * a prototyped (s32) decl is `conflicting types` in this TU. Arg still + * passes in $a0 under default promotions -> codegen unchanged. */ + +void func_80151664(void) { + register s32 a0v __asm__("$4"); + s32 s0; + s32 v1; + + s0 = a0v; + func_80154A74(s0, 0x11); + func_801553C0(s0); + func_801470AC((s32 *)s0); + func_801472B4((void *)s0); + v1 = *(s32 *)(s0 + 0x20); + *(s16 *)(s0 + 0x3E) = 0; + *(s16 *)(s0 + 0x40) = 0; + *(s16 *)(s0 + 0x42) = 0; + *(s8 *)(s0 + 0xDD) = 0; + *(s16 *)(s0 + 0x3C) = *(u16 *)(s0 + 0x3C) & 0xFFFE; + *(s16 *)(v1 + 0x10) = 0; + *(s16 *)(s0 + 0x60) = 0x1000; + *(s16 *)(s0 + 0x62) = 0x1000; + *(s16 *)(s0 + 0x64) = 0x1000; + func_801477E8((s32 *)s0, 0); + func_80153C18(s0); +} + DEFINE_func_801516F0() /* dedup: shared engine-core @0x801516f0 (src/shared) */ diff --git a/src/ov_SC07_011/ov_SC07_011_jr_80154C24.c b/src/ov_SC07_011/ov_SC07_011_jr_80154C24.c index d95af7f8ec..4a4ee3feb1 100644 --- a/src/ov_SC07_011/ov_SC07_011_jr_80154C24.c +++ b/src/ov_SC07_011/ov_SC07_011_jr_80154C24.c @@ -2276,7 +2276,62 @@ DEFINE_func_801564B0() /* dedup: shared engine-core @0x801564b0 (src/shared) */ DEFINE_func_801565C0() /* dedup: shared engine-core @0x801565c0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_80154C24", func_80156600); + +// @class: MATCH (18 ins) — match_one, asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_80154C24 +// +// RECONCILE (uc2 -> uc3). The body is UNCHANGED and still MATCHes; only the declaration +// environment moved. +// +// Gate error: `conflicting types for func_80156600 || previous declaration`. The conflicting +// symbol is func_80156600 ITSELF. ov_SC01_077_jr_80154C24.c:1747 instantiates +// DEFINE_func_80157EA4(), whose macro body (src/shared/engine_core.h:11395) carries +// extern s32 func_80156600(void *a0); +// and calls it at engine_core.h:11409 as `v1 = (s32 *)func_80156600(a0);`. +// The byte-true body is NULLARY — the target zeroes $a0 at entry and uses it as the loop +// counter — so `s32 (void)` vs the header's `s32 (void *)` is a PARAM/ARITY disagreement on +// our own definition. Neither declaration can be changed: the header decl is fleet-shared +// (137 siblings instantiate the same macro), and adding a phantom `void *` parameter to the +// definition perturbs the $a0 allocation. +// +// Escape: §37/§124 ASM-LABEL ALIAS. The definition is given the private C name aF80156600 +// and the assembler label "func_80156600", so it never collides with the canonical +// declaration while still emitting the required symbol for the macro's call site. Zero +// header edits, zero codegen change (verified: cc1 on the real spliced TU is diagnostic-free, +// and match_one still prints MATCH). This declaration MUST travel with the body. +// +// Everything else is as in uc2: externs at BLOCK scope (rule D — keeps the bank travellable to +// the 137 siblings; the sibling func_80156670 declares the same two symbols at function scope +// in the same TU at :1427/:1432), and D_80126AF0 typed as plain u8[] because the body only +// ever does pointer arithmetic on it (+8/entry) — the file-scope `extern S8 D_80126AF0[];` +// form needs engine_types.h, which common.h does not pull in. +// +// Codegen shape: a counter in $a0, a byte cursor $a1 walking D_80126AF0 by 8, a word cursor +// $v1 walking D_801151E0 by 4; the `addu $v0,$a1,$zero` return value sits in the bnez delay +// slot, and the loop fallthrough re-zeroes $v0 for the not-found return. +s32 aF80156600(void) __asm__("func_80156600"); + +s32 aF80156600(void) +{ + extern u8 D_80126AF0[]; + extern s32 D_801151E0[]; + s32 i; + u8 *p; + s32 *q; + + i = 0; + p = &D_80126AF0[0]; + q = &D_801151E0[0]; + do { + if (*q & 1) { + return (s32)p; + } + p += 8; + i += 1; + q += 1; + } while ((u32)i < 3); + return 0; +} + DEFINE_func_80156648() /* dedup: shared engine-core @0x80156648 (src/shared) */ diff --git a/src/ov_SC07_011/ov_SC07_011_jr_8015C32C.c b/src/ov_SC07_011/ov_SC07_011_jr_8015C32C.c index cf178dd185..255cff6add 100644 --- a/src/ov_SC07_011/ov_SC07_011_jr_8015C32C.c +++ b/src/ov_SC07_011/ov_SC07_011_jr_8015C32C.c @@ -4278,7 +4278,58 @@ DEFINE_func_80161278() /* dedup: shared engine-core @0x80161278 (src/shared) */ DEFINE_func_801612B8() /* dedup: shared engine-core @0x801612b8 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_8015C32C", func_8016130C); +extern void func_80161240(void *a0); +extern s32 func_801612B8(void * a0, s32 a1); +#define SHB(x) __asm__ __volatile__("" : "=r"(x) : "0"(x)) +#define SHB(x) __asm__("" : "=r"(x) : "0"(x)) + +extern void func_801599A4(void*); +extern void func_8015BDD0(s32*); +extern void func_8015BF48(s32*); + +/* REDRAFT (decay axis: RETURN TYPE). The stored draft had been flipped to + * `void` to fit the fleet header decl in src/shared/engine_core.h, while the + * body still `return 1; / return 0;`. gcc-2.7.2 silently drops the $v0 sets in + * that situation, costing the three `addiu $v0, $zero, 1` / `addu $v0,$zero,$zero` + * instructions the ASM plainly has. Restored to s32 (§109/§85 — the single call + * site `func_8016130C(a0, func_801612B8(a0, 1));` discards the result, so the + * void->s32 widen is caller-neutral and must be fixed on the HEADER side). */ + +/* RECONCILE (§37/§124 asm-label alias — zero header touch). + * Gate error: jr_8015C32C.c:3501 conflicting types for `func_8016130C' + * :3479 previous declaration. + * TU line 3479 is `DEFINE_func_80161240()`, whose expansion (engine_core.h:3781) + * declares `extern void func_8016130C(void *a0, s32 a1);` — a RETURN-TYPE + * disagreement with this draft's required `s32`. The third line the gate quoted + * (:5228 "location of previous definition") is unrelated noise: it is the + * pre-existing `#define SHB(x)` redefinition *warning* between TU lines 5227 and + * 5538, present with or without this draft. + * Fix: the C identifier becomes aF8016130C, so it never collides with the + * canonical `void func_8016130C` declaration, while the GNU asm label makes the + * EMITTED symbol `func_8016130C`. The macro's caller func_80161240 keeps calling + * the `extern void` spelling and is byte-unaffected (it discards the return). + * Codegen for this function is untouched — match_one still MATCH. + * (Alternative, house style, NOT taken here per the no-header-edit rule: widen + * src/shared/engine_core.h:3781 to `extern s32 func_8016130C(void *a0, s32 a1);` + * and rename back to the plain name — byte-identical either way.) */ + +s32 aF8016130C(void *a0, s32 a1) __asm__("func_8016130C"); + +s32 aF8016130C(void *a0, s32 a1) +{ + if ((a1 == 0) || (a1 == 0x8000)) { + func_801599A4(a0); + ((void (*)(void *))func_8015BDD0)(a0); + return 1; + } + if ((a1 & 0x4000) != 0) { + func_801599A4(a0); + ((void (*)(void *))func_8015BF48)(a0); + return 1; + } + return 0; +} + // @class: schedule @@ -4584,7 +4635,30 @@ DEFINE_func_80161B84() /* dedup: shared engine-core @0x80161b84 (src/shared) */ DEFINE_func_80161BE0() /* dedup: shared engine-core @0x80161be0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_8015C32C", func_80161C24); + +extern void func_80147324(); + +/* RECONCILE (§17a-1/§20): the TU already declares + * extern void func_80161C24(s32, s32); (jr_8015C32C.c:1474 / :1417) + * so the definition must use (int, int); the unsigned semantics of param_2 + * are restored by a cast at each use (codegen-neutral: sltiu + index math). */ +void func_80161C24(int param_1, int param_2) +{ + + extern unsigned short D_80181138[]; + extern unsigned short D_8018113A[]; + + if ((unsigned int)param_2 < 8) { + if (*(u8 *)(param_1 + 0xDA) != 0) { + func_80147324(D_80181138[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 0; + } else { + func_80147324(D_8018113A[(unsigned int)param_2 * 2]); + *(u8 *)(param_1 + 0xDA) = 1; + } + } +} + // @class: struct @@ -4603,7 +4677,58 @@ void func_80161C98(int param_1, u32 param_2) } -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_8015C32C", func_80161CD0); + +// @class: plumbing +// @stuck: none — MATCH (20 ins) +// DECAY AXIS: return type. The stored draft declared `s32 func_80161CD0(...)` with no +// return statement. That kept $v0 live-out at the epilogue, so gcc-2.7.2's delayed-branch +// pass refused to speculatively fill the second `beqz $v0` delay slot from the fall-through +// with `sll $v0, $s0, 1` — costing one extra nop (21 vs 20 ins) and shifting both branch +// displacements. Declaring the function `void` makes $v0 dead at the return and the fill +// happens. Same return-type axis as the wave, opposite direction (s32 -> void). +// param_2 must be UNSIGNED: the guard assembles as `sltiu $v0, $s0, 0x8`. +// +// RECONCILE (§37/§124 asm-label alias) — the ONLY change vs the uc2 draft; the body is +// byte-identical and untouched. +// The real TU src/ov_SC01_077/ov_SC01_077_jr_8015C32C.c already carries the m2c canonical +// declaration +// :23 extern s32 func_80161CD0(s32 a0, s32 a1); /* match-first, arity 2 */ +// :1207 extern s32 func_80161CD0(s32 a0, s32 a1); +// so a plain `void func_80161CD0(int, unsigned int)` definition dies with +// conflicting types for `func_80161CD0' / previous declaration ... (rtu_match CC1 FAIL). +// This is the RETURN axis (s32 vs void) plus the param-2 signedness axis, and BOTH spellings +// are load-bearing for the 20-instruction codegen — conforming the definition to the +// declaration (escape #1) would perturb bytes, so it is not available here. +// The alias fixes it at T0: the C identifier is aF80161CD0 (no declaration to collide with) +// while the EMITTED SYMBOL is func_80161CD0. In-TU callers keep using the `extern s32` +// spelling and are byte-unaffected (they discard the return). Zero tracked files touched; +// no header edit is even possible/needed — grep shows func_80161CD0 has NO declaration in +// include/ or src/shared/, the conflict is purely TU-local m2c boilerplate. +// Exactly the pattern already banked at src/ov_SC03_099/ov_SC03_099_jr_8015C32C.c:3503 +// for the sibling func_8016191C. +// +// NOTE on the third gate diagnostic (`:5223 note: location of previous definition`): that is +// a RED HERRING, unrelated to this function. It is the pre-existing CPP note paired with +// `:5533 warning: "SHB" redefined` — the TU defines the SHB scheduling-barrier macro twice +// (post-splice :5222 with __volatile__, :5533 without). It is a warning, present with or +// without this draft, and it does not fail cc1. + +extern int func_800CF8B4(); +extern void func_80147324(int arg0); + +void aF80161CD0(int param_1, unsigned int param_2) __asm__("func_80161CD0"); + +void aF80161CD0(int param_1, unsigned int param_2) +{ + + extern unsigned short D_80181168; + if (param_2 < 8) { + if (func_800CF8B4() != 0) { + func_80147324((&D_80181168)[param_2]); + } + } +} + // @class: struct @@ -7393,7 +7518,22 @@ void func_80168AA8(void *a0) { DEFINE_func_80168AE4() /* dedup: shared engine-core @0x80168ae4 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_8015C32C", func_80168B70); + +extern void func_80168BDC(s32 a0, s32 a1, s32 a2, s32 a3); +/* Conform to the TU's canonical decl (jr_8015C32C.c:5546 `extern void ((void (*)(void))func_80146C3C)(void);`) + * and cast at the use site — same escape the TU already uses at :6388. Codegen-neutral. */ +extern void func_80146C3C(u8*); + +void func_80168B70(s32 a0) { + s32 *p = (s32 *)a0; + if (--p[0x1C / 4] != -1) { + func_80168BDC(a0, 9, 3, 1); + } else { + func_80168BDC(a0, 9, 2, 1); + ((void (*)(s32))func_80146C3C)(a0); + } +} + extern void func_800D2318(void); extern void RotMatrixYXZ(void *a0, void *a1); diff --git a/src/ov_SC07_011/ov_SC07_011_jr_8016AE5C.c b/src/ov_SC07_011/ov_SC07_011_jr_8016AE5C.c index 12ae83f405..00d7b6b71d 100644 --- a/src/ov_SC07_011/ov_SC07_011_jr_8016AE5C.c +++ b/src/ov_SC07_011/ov_SC07_011_jr_8016AE5C.c @@ -54,7 +54,6 @@ extern void func_80129C40(s32 a0); extern void func_800D0630(void); extern void func_80145CEC(void); extern void func_80144B9C(void); -extern u8 D_800B9A17; extern u8 D_800B9A10; extern void func_80128420(void); extern s32 func_800D0588(void); @@ -1399,7 +1398,6 @@ extern void func_8014B768(s32 a0, s32 a1); extern void func_8014B944(s32 a0, s32 a1, s32 a2); extern s32 D_80078EA4; extern u16 D_80078EB2; -extern s16 D_80078EB4; extern void func_8014BB0C(void); extern void func_8014BC0C(s32 a0, s32 a1); extern void func_8014BC44(s32 a0, s32 a1); @@ -4673,7 +4671,63 @@ DEFINE_func_8016F094() /* dedup: shared engine-core @0x8016f094 (src/shared) */ INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_8016AE5C", func_8016F0AC); -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_8016AE5C", func_8016F0E4); + +/* RECONCILE NOTE (uc3) — the body is byte-unchanged from uc2; only the declaration + * environment moved. The gate error was + * jr_8016AB6C.c:4183 conflicting types for func_8016F0E4 || :4089 previous declaration + * :4089 was THIS definition; :4183 is the `extern void func_8016F0E4(void);` carried inside + * DEFINE_func_8016F4C4() (src/shared/engine_core.h:22579), which the host TU instantiates at + * ov_SC01_077_jr_8016AB6C.c:4136 — i.e. AFTER the definition, so it is a hard error, not the + * mere warning produced by the earlier block-scope decl in func_8016F0AC (TU:4047). + * Byte-true signature is `void (u8 *)`; the fleet canon is `void (void)` — a PARAM/ARITY + * disagreement on func_8016F0E4 ITSELF. + * + * ESCAPE TAKEN: §37/§124 ASM-LABEL ALIAS (escape 2, T0 draft-only, zero tracked-file edits). + * The C identifier is aF8016F0E4 so this TU declares func_8016F0E4 exactly once (the macro's + * own `extern void`), while the emitted SYMBOL is func_8016F0E4. Both existing callers already + * launder through a cast or take no args, so neither is perturbed: + * - func_8016F0AC (TU:4047-4049) casts: ((void (*)(void *))func_8016F0E4)(&D_80126B58); + * - DEFINE_func_8016F4C4 calls func_8016F0E4() with 0 args against its own (void) prototype. + * cc1-proven: preprocess+compile of the real TU with this body spliced in is free of any + * func_8016F0E4 diagnostic (the pre-existing unrelated warnings in the TU are unchanged). + * + * This SUPERSEDES uc2's //@EDIT de-macroize plan (§65b) and its T2 engine_core.h alternative + * (§75c) — neither is needed; nothing outside this unit changes, so the bank travels to the + * 137 siblings as a plain unit (carry the alias DECLARATION line with it — §124 trap 2). + */ + +extern void func_8017196C(s32 *a0, s8 a1); /* == DEFINE_func_8017196C's own signature */ +extern void func_80147060(u8 *a0); +extern void func_801511A8(u8 *a0); +extern void func_80148634(void *a0); + +// @class: plumbing +// @stuck: none — MATCH (26 ins). +// Levers: (1) §42b read-global &-cast — the TU declares D_80078EB4 as s16 but the target +// loads it with `lhu`; the bare `D_80078EB4 != 0` gives `lh` (probed: closeness 1, WIDTH +// lh!=lhu), so read it as *(u16 *)&D_80078EB4. (2) both 0x1F8/0x1FC stores are s32 `sw`, +// emitted before the first jal (the 0x1FC one lands in the delay slot by sched2 — free). +// (3) the param needs no laundering: it is copied to $s0 at entry and the first call's +// `move $a0,$s0` is deleted because $a0 already holds it — plain `u8 *p` reproduces this. +// (4) §37/§124 asm-label alias — see the RECONCILE NOTE above; codegen-neutral (symbol only). + +void aF8016F0E4(u8 *p) __asm__("func_8016F0E4"); + +void aF8016F0E4(u8 *p) { + + extern s16 D_80078EB4; /* fleet-canonical spelling; see the &-cast below */ + extern u8 D_800B9A17; + *(s32 *)(p + 0x1F8) = 0; + *(s32 *)(p + 0x1FC) = 0; + func_8017196C((s32 *)p, 0); + func_80147060(p); + func_801511A8(p); + if (*(u16 *)&D_80078EB4 != 0) { + D_800B9A17 = 1; + } + func_80148634(p); +} + DEFINE_func_8016F14C() /* dedup: shared engine-core @0x8016f14c (src/shared) */ @@ -5135,7 +5189,28 @@ void func_80170810(void *a0) { DEFINE_func_8017084C() /* dedup: shared engine-core @0x8017084c (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_8016AE5C", func_801708B0); + +extern void func_801484B0(s32 a0, s32 a1); +extern void func_80149350(s32 arg0); +extern s32 func_8014C168(s32*, s32); +extern void func_80171928(void *a0); + +void func_801708B0(void* a0) +{ + + extern void *D_8011F738; + u16 sp10[4]; + u16 sp18[4]; + short *r; + func_801484B0((s32)&sp10, (s32)&sp18); + ((void (*)(s32, void *, void *))func_80149350)((s32)a0, &sp10, &sp10); + r = (short *)((s32 (*)(s32, s32))func_8014C168)((s32)a0, (*(s32 *)&D_8011F738)); + r[3] = sp10[0]; + r[5] = sp10[1]; + r[7] = sp10[2]; + func_80171928((void *)a0); +} + DEFINE_func_80170928() /* dedup: shared engine-core @0x80170928 (src/shared) */ @@ -5200,7 +5275,40 @@ void func_80170C74(void *a0) { DEFINE_func_80170CB0() /* dedup: shared engine-core @0x80170cb0 (src/shared) */ -INCLUDE_ASM("asm/ov_SC07_011/nonmatchings/ov_SC07_011_jr_8016AE5C", func_80170CF0); + +extern void func_801718AC(void *a0); + +void func_80170CF0(void* a0) +{ + + extern void *D_8011F738; + extern s32 D_8011F73C; + extern s32 D_8011F740; + s32 v1; + void *p; + ((void (*)(s32))D_8011F738)(a0); + v1 = *(u8 *)(a0 + 0x217); + if (v1 == 1) { + goto case_1; + } + if (v1 <= 1) { + goto done; + } + if (v1 == 2) { + goto case_2; + } + goto done; +case_1: + p = (*(void * *)&D_8011F73C); + goto call; +case_2: + p = (*(void * *)&D_8011F740); +call: + func_801718AC(p); +done: + ; +} +