# E01-S05: prove cfg/compiler.cfg capacity checks, then run the map skeleton # in VICE x128 and verify the fixed test mailbox at $1BF0. [CmdletBinding()] param( [int]$RemotePort = 6513, [int]$TimeoutSeconds = 30 ) $ErrorActionPreference = "Stop" $project = Split-Path $PSScriptRoot -Parent $build = Join-Path $project "build" $program = Join-Path $build "map-skeleton.prg" $screenshot = Join-Path $build "map-skeleton-vicii.png" $x128 = "C:\Program Files\GTK3VICE-3.10-win64\bin\x128.exe" $mailbox = 0x1BF0 # 1. Every bounded region must reject an overflow at link time. foreach ($region in "ZP", "COMMON", "MAIN", "SWSTACK", "TABLES") { & (Join-Path $PSScriptRoot "build-map-skeleton.ps1") -Overflow $region } # 2. The real skeleton. & (Join-Path $PSScriptRoot "build-map-skeleton.ps1") if (-not (Test-Path -LiteralPath $x128)) { throw "x128.exe not found at $x128" } if (Test-Path -LiteralPath $screenshot) { Remove-Item -LiteralPath $screenshot -Force } $arguments = @( "-autostart", ('"' + $program + '"'), "-autostart-warp", "-warp", "-sounddev", "dummy", "-exitscreenshotvicii", ('"' + $screenshot + '"'), "-remotemonitor", "-remotemonitoraddress", ("ip4://127.0.0.1:{0}" -f $RemotePort) ) -join ' ' $psi = [Diagnostics.ProcessStartInfo]::new() $psi.FileName = $x128 $psi.Arguments = $arguments $psi.UseShellExecute = $true $psi.WindowStyle = [Diagnostics.ProcessWindowStyle]::Hidden $process = [Diagnostics.Process]::Start($psi) $client = $null function Read-Available($stream) { $buffer = New-Object byte[] 16384 $text = "" Start-Sleep -Milliseconds 300 while ($stream.DataAvailable) { $count = $stream.Read($buffer, 0, $buffer.Length) if ($count -le 0) { break } $text += [Text.Encoding]::ASCII.GetString($buffer, 0, $count) Start-Sleep -Milliseconds 50 } return $text } function Convert-Dump([string]$text) { $memory = @{} $rows = [regex]::Matches($text, "(?i)C:([0-9a-f]{4})((?:[ ]{1,2}[0-9a-f]{2}){1,16})") foreach ($row in $rows) { $address = [Convert]::ToInt32($row.Groups[1].Value, 16) foreach ($hex in ($row.Groups[2].Value.Trim() -split '\s+')) { $memory[$address] = [Convert]::ToInt32($hex, 16) $address++ } } return $memory } try { $deadline = [DateTime]::UtcNow.AddSeconds(10) while ([DateTime]::UtcNow -lt $deadline) { if ($process.HasExited) { throw "x128 exited before remote verification" } try { $client = [Net.Sockets.TcpClient]::new() $client.Connect("127.0.0.1", $RemotePort) break } catch { if ($client) { $client.Dispose(); $client = $null } Start-Sleep -Milliseconds 200 } } if (-not $client -or -not $client.Connected) { throw "could not connect to VICE remote monitor" } $stream = $client.GetStream() $writer = [IO.StreamWriter]::new($stream, [Text.Encoding]::ASCII, 1024, $true) $writer.NewLine = "`n" $writer.AutoFlush = $true # Mailbox protocol: magic "N8", then status leaves RUNNING ($01). $memory = @{} $deadline = [DateTime]::UtcNow.AddSeconds($TimeoutSeconds) while ($true) { Start-Sleep -Seconds 1 $writer.WriteLine(("m `${0:X4} `${1:X4}" -f $mailbox, ($mailbox + 15))) $chunk = Read-Available $stream $memory = Convert-Dump $chunk $magic = ($memory[$mailbox] -eq 0x4E -and $memory[$mailbox + 1] -eq 0x38) if ($magic -and $memory[$mailbox + 3] -in 0xA5, 0xFA) { break } if ([DateTime]::UtcNow -ge $deadline) { throw "mailbox never reached a final status; last response: $chunk" } $writer.WriteLine("x") Read-Available $stream | Out-Null } $writer.WriteLine("x") Start-Sleep -Seconds 2 # let the OK line print $writer.WriteLine("m 0000 0000") Read-Available $stream | Out-Null $bytes = 0..15 | ForEach-Object { "{0:X2}" -f $memory[$mailbox + $_] } $code = $memory[$mailbox + 4] + 256 * $memory[$mailbox + 5] $writer.WriteLine("quit") $writer.Dispose(); $client.Dispose(); $client = $null if (-not $process.WaitForExit(10000)) { throw "x128 did not exit after monitor quit" } Write-Host ("mailbox `$1BF0: {0}" -f ($bytes -join ' ')) $checks = @(@(0x01, "ZP"), @(0x02, "KCALL"), @(0x04, "BANK1"), @(0x08, "NMI"), @(0x10, "COMMON"), @(0x20, "RESTORE")) $failed = @($checks | Where-Object { $code -band $_[0] } | ForEach-Object { $_[1] }) if ($memory[$mailbox + 2] -ne 0x01) { throw "producer is not the platform skeleton" } if ($memory[$mailbox + 3] -ne 0xA5 -or $code -ne 0) { throw ("skeleton FAILED checks: {0} (code `${1:X4})" -f ($failed -join ', '), $code) } $nmis = $memory[$mailbox + 8] if ($nmis -lt 1) { throw "no NMI was counted" } Write-Host ("verified mailbox PASS, code `$0000, {0} NMI(s) under CR `${1:X2} / P0 page `${2:X2}, ZP sentinel `${3:X2}" -f $nmis, $memory[$mailbox + 9], $memory[$mailbox + 10], $memory[$mailbox + 11]) if (Test-Path -LiteralPath $screenshot) { Write-Host ("screenshot: {0}" -f $screenshot) } } finally { if ($client) { $client.Dispose() } if ($process -and -not $process.HasExited) { $process.Kill(); $process.WaitForExit() } }