237 lines
9.5 KiB
PowerShell
237 lines
9.5 KiB
PowerShell
<#
|
||
.SYNOPSIS
|
||
Build multiple ISR frequency / command budget variants for A/B testing.
|
||
Each variant produces a separate tsconf_*.img disk image.
|
||
|
||
.DESCRIPTION
|
||
Generates inc\variant_cfg.h for each variant, runs build.bat, copies
|
||
the output image. Restores the original variant_cfg.h afterward.
|
||
|
||
NO source files are regex-patched — only variant_cfg.h is overwritten.
|
||
|
||
.NOTES
|
||
C compilation is SLOW (~60 sec per variant). Do NOT interrupt (Ctrl-C).
|
||
#>
|
||
|
||
Set-StrictMode -Version Latest
|
||
$ErrorActionPreference = 'Stop'
|
||
|
||
$root = Split-Path -Parent $MyInvocation.MyCommand.Path # src_sdcc
|
||
$projRoot = Split-Path -Parent $root # project root
|
||
$cfgFile = "$root\inc\variant_cfg.h"
|
||
|
||
# ── Frequency tiers ──────────────────────────────────────────────────
|
||
# ISR_FREQ = 3 500 000 / Step; Entries = 71 680 / Step; TPF = Entries
|
||
# Shift = log2(44100 / Freq) rounded down
|
||
$freqTiers = @{
|
||
'2734' = @{ Tag=''; Freq=2734; Step=1280; Entries=56; Shift=4; Mask='0x0F'; TPF=56; Budgets=@(8,16) }
|
||
'1367' = @{ Tag='hf'; Freq=1367; Step=2560; Entries=28; Shift=5; Mask='0x1F'; TPF=28; Budgets=@(8,16,32) }
|
||
'683' = @{ Tag='qf'; Freq=683; Step=5120; Entries=14; Shift=6; Mask='0x3F'; TPF=14; Budgets=@(16,32,64) }
|
||
}
|
||
|
||
# ── Generate variants: 2+3+3 = 8 base × 2 NS = 16 total ─────────────
|
||
$baseVariants = @()
|
||
foreach ($ft in $freqTiers.Values | Sort-Object Freq -Descending) {
|
||
foreach ($b in $ft.Budgets) {
|
||
$suffix = if ($ft.Tag) { "_$($ft.Tag)" } else { '' }
|
||
$name = "b${b}${suffix}"
|
||
$baseVariants += @{
|
||
Name = $name
|
||
Budget = $b
|
||
Entries = $ft.Entries
|
||
Step = $ft.Step
|
||
Freq = $ft.Freq
|
||
Shift = $ft.Shift
|
||
Mask = $ft.Mask
|
||
TPF = $ft.TPF
|
||
DataLoc = '0xB600'
|
||
NS = $false
|
||
}
|
||
}
|
||
}
|
||
|
||
# Auto-generate _ns (no short waits) clones
|
||
$nsVariants = foreach ($bv in $baseVariants) {
|
||
$nv = $bv.Clone()
|
||
$nv.Name = $bv.Name + '_ns'
|
||
$nv.NS = $true
|
||
$nv
|
||
}
|
||
$variants = $baseVariants + $nsVariants
|
||
|
||
# ── Save original variant_cfg.h ─────────────────────────────────────
|
||
$cfgBackup = Get-Content $cfgFile -Raw
|
||
|
||
function Restore-Cfg {
|
||
Set-Content $script:cfgFile -Value $script:cfgBackup -NoNewline
|
||
}
|
||
|
||
# ── Helper: generate variant_cfg.h ──────────────────────────────────
|
||
function Write-VariantCfg($v) {
|
||
$nsLine = if ($v.NS) { "#define VGM_NO_SHORT_WAITS" } else { "/* #define VGM_NO_SHORT_WAITS */" }
|
||
$content = @"
|
||
/* variant_cfg.h -- AUTO-GENERATED by build_variants.ps1
|
||
* Variant: $($v.Name) Freq=$($v.Freq)Hz Budget=$($v.Budget) NS=$($v.NS)
|
||
*/
|
||
#ifndef VARIANT_CFG_H
|
||
#define VARIANT_CFG_H
|
||
|
||
#define ISR_FREQ $($v.Freq)
|
||
#define ISR_TICKS_PER_FRAME $($v.TPF)
|
||
#define VGM_SAMPLE_SHIFT $($v.Shift)
|
||
#define VGM_SAMPLE_MASK $($v.Mask)
|
||
#define VGM_FILL_CMD_BUDGET $($v.Budget)
|
||
|
||
#define VARIANT_POS_ENTRIES $($v.Entries)
|
||
#define VARIANT_POS_STEP $($v.Step)
|
||
#define VARIANT_DATA_LOC $($v.DataLoc)
|
||
|
||
$nsLine
|
||
|
||
#endif /* VARIANT_CFG_H */
|
||
"@
|
||
Set-Content $script:cfgFile -Value $content -NoNewline
|
||
}
|
||
|
||
# ── Output directory ─────────────────────────────────────────────────
|
||
$outDir = "$projRoot\variants"
|
||
if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir | Out-Null }
|
||
|
||
# ── Build loop ──────────────────────────────────────────────────────
|
||
$total = $variants.Count
|
||
$idx = 0
|
||
$results = @()
|
||
|
||
foreach ($v in $variants) {
|
||
$idx++
|
||
$name = $v.Name
|
||
Write-Host "`n========== [$idx/$total] Building $name ==========" -ForegroundColor Cyan
|
||
Write-Host " Budget=$($v.Budget) Freq=$($v.Freq)Hz Entries=$($v.Entries) Step=$($v.Step) Shift=$($v.Shift) TPF=$($v.TPF) NS=$($v.NS)"
|
||
|
||
# --- Generate variant_cfg.h ---
|
||
Write-VariantCfg $v
|
||
|
||
# --- Clean .rel files to force recompile ---
|
||
Remove-Item "$root\build\*.rel" -ErrorAction SilentlyContinue
|
||
|
||
# --- Run build ---
|
||
Write-Host " Building... (this takes ~60 sec)" -ForegroundColor Yellow
|
||
$buildOut = cmd /c "cd /d $root && build.bat 2>&1"
|
||
$buildOk = $buildOut | Where-Object { $_ -match 'BUILD OK' }
|
||
$buildFail = $buildOut | Where-Object { $_ -match 'FAIL|ERROR|overflow' }
|
||
|
||
if ($buildFail -or -not $buildOk) {
|
||
Write-Host " *** BUILD FAILED for $name ***" -ForegroundColor Red
|
||
$buildOut | Where-Object { $_ -match 'FAIL|error|warning|_CODE|_DATA|Last byte|overflow|variant_cfg' } |
|
||
ForEach-Object { Write-Host " $_" }
|
||
$results += [pscustomobject]@{ Name=$name; Status='FAIL'; Code=0; Free=0 }
|
||
continue
|
||
}
|
||
|
||
# --- Extract sizes from output ---
|
||
$codeLine = $buildOut | Where-Object { $_ -match '_CODE' } | Select-Object -First 1
|
||
$freeLine = $buildOut | Where-Object { $_ -match 'Free before' } | Select-Object -First 1
|
||
$codeSize = if ($codeLine -match '(\d+)\s+bytes') { [int]$Matches[1] } else { 0 }
|
||
$freeSize = if ($freeLine -match 'Free before C000:\s*(\d+)') { [int]$Matches[1] } else { 0 }
|
||
|
||
# --- Show map ---
|
||
$buildOut | Where-Object { $_ -match 'variant_cfg:|Section|_CODE|_DATA|_GSINIT|_HOME|_INITIAL|Last byte|Code:|Written.*WMF' } |
|
||
ForEach-Object { Write-Host " $_" }
|
||
|
||
# --- Copy output image ---
|
||
$srcImg = "$projRoot\tsconf.img"
|
||
$dstImg = "$outDir\tsconf_$name.img"
|
||
if (Test-Path $srcImg) {
|
||
Copy-Item $srcImg $dstImg -Force
|
||
Write-Host " -> $dstImg" -ForegroundColor Green
|
||
} else {
|
||
Write-Host " WARNING: tsconf.img not found" -ForegroundColor Yellow
|
||
}
|
||
# Also copy WMF
|
||
$srcWmf = "$projRoot\DiskRef\WC\VGMPLAY.WMF"
|
||
$dstWmf = "$outDir\VGMPLAY_$name.WMF"
|
||
if (Test-Path $srcWmf) {
|
||
Copy-Item $srcWmf $dstWmf -Force
|
||
}
|
||
|
||
$results += [pscustomobject]@{ Name=$name; Status='OK'; Code=$codeSize; Free=$freeSize }
|
||
}
|
||
|
||
# ── Restore original variant_cfg.h ──────────────────────────────────
|
||
Write-Host "`n========== Restoring original variant_cfg.h ==========" -ForegroundColor Cyan
|
||
Restore-Cfg
|
||
# Rebuild baseline to leave workspace clean
|
||
Remove-Item "$root\build\*.rel" -ErrorAction SilentlyContinue
|
||
Write-Host " Rebuilding baseline..." -ForegroundColor Yellow
|
||
$null = cmd /c "cd /d $root && build.bat 2>&1"
|
||
Write-Host " Done." -ForegroundColor Green
|
||
|
||
# ── Summary ──────────────────────────────────────────────────────────
|
||
Write-Host "`n========== VARIANT SUMMARY ==========" -ForegroundColor Cyan
|
||
$fmt = "{0,-18} {1,6} {2,6} {3,6} {4,3} {5,5} {6,5}"
|
||
Write-Host ($fmt -f 'Name','Budget','Freq','Code','NS','Free','St')
|
||
Write-Host ($fmt -f '----','------','----','----','--','----','--')
|
||
foreach ($r in $results) {
|
||
$v = $variants | Where-Object { $_.Name -eq $r.Name }
|
||
$nsTag = if ($v.NS) { 'yes' } else { ' - ' }
|
||
$color = if ($r.Status -eq 'OK') { 'Green' } else { 'Red' }
|
||
Write-Host ($fmt -f $r.Name, $v.Budget, $v.Freq, $r.Code, $nsTag, $r.Free, $r.Status) -ForegroundColor $color
|
||
}
|
||
Write-Host "`nImages in: $outDir"
|
||
|
||
# ── Generate README.txt ──────────────────────────────────────────────
|
||
$readme = @"
|
||
Variants of VGM player for A/B testing
|
||
=======================================
|
||
|
||
Parameters stored in inc\variant_cfg.h (single config file).
|
||
build_variants.ps1 regenerates variant_cfg.h for each variant.
|
||
|
||
Frequency tiers:
|
||
qf - quarter freq (ISR 683 Hz, step 5120, 14 ticks/frame)
|
||
(none) - base freq (ISR 2734 Hz, step 1280, 56 ticks/frame)
|
||
hf - half freq (ISR 1367 Hz, step 2560, 28 ticks/frame)
|
||
|
||
Other tags:
|
||
b<N> - command budget (max commands between forced pauses)
|
||
ns - no short waits (0x70-0x7F pauses skipped)
|
||
|
||
"@
|
||
|
||
$readme += "{0,-30} {1,6} {2,8} {3,3} {4,6} {5,5} {6}`n" -f 'Image','Budget','Freq','NS','Code','Free','Status'
|
||
$readme += "{0,-30} {1,6} {2,8} {3,3} {4,6} {5,5} {6}`n" -f '-----','------','----','--','----','----','------'
|
||
foreach ($r in $results) {
|
||
$v = $variants | Where-Object { $_.Name -eq $r.Name }
|
||
$nsTag = if ($v.NS) { 'yes' } else { ' - ' }
|
||
$freq = "$($v.Freq)Hz"
|
||
$readme += "{0,-30} {1,6} {2,8} {3,3} {4,6} {5,5} {6}`n" -f "tsconf_$($r.Name).img", $v.Budget, $freq, $nsTag, $r.Code, $r.Free, $r.Status
|
||
}
|
||
|
||
$readme += @"
|
||
|
||
What to test
|
||
------------
|
||
1. Higher budget helps (b32/b48/b64 vs b8/b16)
|
||
-> Increase VGM_FILL_CMD_BUDGET.
|
||
|
||
2. Lower freq helps (hf or qf better than base)
|
||
-> ISR overhead is the bottleneck, reduce frequency.
|
||
|
||
3. Higher freq helps (base better than hf/qf)
|
||
-> More granular timing needed, keep higher ISR.
|
||
|
||
4. _ns variants better -> short waits 0x70-0x7F are the problem.
|
||
|
||
5. Nothing helps -> something else (OPL3 write delay, VGM parsing, etc.)
|
||
"@
|
||
|
||
Set-Content "$outDir\README.txt" -Value $readme -NoNewline -Encoding UTF8
|
||
Write-Host "`nREADME.txt written to $outDir" -ForegroundColor Green
|
||
|
||
Write-Host "`nLegend:" -ForegroundColor DarkGray
|
||
Write-Host " qf = quarter freq (683 Hz)" -ForegroundColor DarkGray
|
||
Write-Host " hf = half freq (1367 Hz)" -ForegroundColor DarkGray
|
||
Write-Host " (no suffix) = base freq (2734 Hz)" -ForegroundColor DarkGray
|
||
Write-Host " bN = budget N commands per ISR tick" -ForegroundColor DarkGray
|
||
Write-Host " ns = no short waits (0x70-0x7F skipped)" -ForegroundColor DarkGray
|