|
- # E01-S02: execute the MMU probe and verify its machine-readable result block.
- [CmdletBinding()]
- param([int]$RemotePort = 6511)
-
- $ErrorActionPreference = "Stop"
- $project = Split-Path $PSScriptRoot -Parent
- $build = Join-Path $project "build"
- $program = Join-Path $build "mmu-probe.prg"
- $labelsPath = Join-Path $build "mmu-probe.lbl"
- $traceCommands = Join-Path $project "monitor\mmu-probe-trace.txt"
- $monitorLog = Join-Path $build "mmu-probe-monitor.log"
- $x128 = "C:\Program Files\GTK3VICE-3.10-win64\bin\x128.exe"
-
- & (Join-Path $PSScriptRoot "build-mmu-probe.ps1")
- if ($LASTEXITCODE -ne 0) { throw "MMU probe build failed" }
- if (Test-Path -LiteralPath $monitorLog) { Remove-Item -LiteralPath $monitorLog -Force }
-
- $labelText = Get-Content -Raw -LiteralPath $labelsPath
- function Get-LabelAddress([string]$name) {
- $match = [regex]::Match($labelText,
- "(?im)^al\s+([0-9a-f]+)\s+\." + [regex]::Escape($name) + "\s*$")
- if (-not $match.Success) { throw "missing label $name" }
- return [Convert]::ToInt32($match.Groups[1].Value, 16)
- }
-
- $resultAddress = Get-LabelAddress "mmu_probe_result"
- $completeAddress = Get-LabelAddress "mmu_probe_complete"
- $bank0Address = Get-LabelAddress "mmu_probe_observed_bank0"
- $bank1Address = Get-LabelAddress "mmu_probe_observed_bank1"
- $p0Address = Get-LabelAddress "mmu_probe_observed_p0"
- $p1Address = Get-LabelAddress "mmu_probe_observed_p1"
- $p0cBank0Address = Get-LabelAddress "mmu_probe_obs_p0c_b0"
- $p1cBank0Address = Get-LabelAddress "mmu_probe_obs_p1c_b0"
- $stateEndAddress = Get-LabelAddress "mmu_probe_common_rcr"
- $entryAddress = Get-LabelAddress "mmu_probe_entry"
- $doneAddress = Get-LabelAddress "mmu_probe_done"
- $codeEndAddress = Get-LabelAddress "mmu_probe_code_end"
-
- # The checked-in trace file has the fixed entry point; the done address moves
- # with the code, so append it from the fresh label file.
- $generatedTrace = Join-Path $build "mmu-probe-trace.gen.txt"
- $traceText = (Get-Content -Raw -LiteralPath $traceCommands) +
- ("trace exec `${0:X4}`ncommand 4 `"disable 2`"`n" -f $doneAddress)
- [IO.File]::WriteAllText($generatedTrace, $traceText, [Text.Encoding]::ASCII)
- $traceCommands = $generatedTrace
-
- $arguments = @(
- "-autostart", ('"' + $program + '"'),
- "-autostart-warp", "-warp", "-sounddev", "dummy",
- "-moncommands", ('"' + $traceCommands + '"'),
- "-monlog", "-monlogname", ('"' + $monitorLog + '"'),
- "-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
-
- 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"
- }
-
- Start-Sleep -Seconds 3
- $stream = $client.GetStream()
- $stream.ReadTimeout = 2000
- $writer = [IO.StreamWriter]::new($stream, [Text.Encoding]::ASCII, 1024, $true)
- $writer.NewLine = "`n"
- $writer.AutoFlush = $true
- $writer.WriteLine(("m `${0:X4} `${1:X4}" -f $resultAddress, $stateEndAddress))
- Start-Sleep -Milliseconds 400
-
- $buffer = New-Object byte[] 16384
- $response = ""
- while ($stream.DataAvailable) {
- $count = $stream.Read($buffer, 0, $buffer.Length)
- if ($count -le 0) { break }
- $response += [Text.Encoding]::ASCII.GetString($buffer, 0, $count)
- Start-Sleep -Milliseconds 50
- }
-
- $expected = @(
- @{ Address = $resultAddress; Value = 0xFF; Name = "result" },
- @{ Address = $completeAddress; Value = 0xA5; Name = "complete" },
- @{ Address = $bank0Address; Value = 0x11; Name = "bank0" },
- @{ Address = $bank1Address; Value = 0x22; Name = "bank1" },
- @{ Address = $p0Address; Value = 0x77; Name = "page0" },
- @{ Address = $p1Address; Value = 0x33; Name = "page1" },
- @{ Address = $p0cBank0Address; Value = 0x66; Name = "page0-common-bank0" },
- @{ Address = $p1cBank0Address; Value = 0x5A; Name = "page1-common-bank0" }
- )
- # Parse every monitor dump row into address -> byte so checks do not
- # depend on where a 16-byte row happens to start.
- $memory = @{}
- $rows = [regex]::Matches($response,
- "(?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++
- }
- }
- foreach ($item in $expected) {
- if (-not $memory.ContainsKey($item.Address) -or
- $memory[$item.Address] -ne $item.Value) {
- throw ("{0} at `${1:X4} was not `${2:X2}; monitor response: {3}" -f
- $item.Name, $item.Address, $item.Value, $response)
- }
- }
-
- $writer.WriteLine("q")
- $writer.Dispose()
- $client.Dispose()
- $client = $null
- if (-not $process.WaitForExit(5000)) { throw "x128 did not exit after monitor quit" }
-
- if (-not (Test-Path -LiteralPath $monitorLog)) { throw "monitor log not created" }
- # Trace hits go to the log before the remote client connects and to the
- # remote stream afterwards; examine both.
- $trace = (Get-Content -Raw -LiteralPath $monitorLog) + "`n" + $response
- [IO.File]::WriteAllText((Join-Path $build "mmu-probe-trace-dump.txt"), $trace)
-
- # Every hit is "#n (Trace <kind> <addr>) ..." followed by ".C:<pc> ...".
- $hits = [regex]::Matches($trace,
- "(?im)^#\d+ \(Trace\s+(exec|store) ([0-9a-f]{4})\)[^\r\n]*\r?\n\.C:([0-9a-f]{4})")
- $inWindow = $false
- $windowClosed = $false
- $storeCount = 0
- $written = @{}
- foreach ($hit in $hits) {
- $kind = $hit.Groups[1].Value.ToLowerInvariant()
- $target = [Convert]::ToInt32($hit.Groups[2].Value, 16)
- $pc = [Convert]::ToInt32($hit.Groups[3].Value, 16)
- if ($kind -eq 'exec') {
- if ($target -eq $entryAddress -and -not $windowClosed) { $inWindow = $true }
- elseif ($target -eq $doneAddress -and $inWindow) {
- $inWindow = $false; $windowClosed = $true; break
- }
- continue
- }
- if (-not $inWindow) { continue }
- if ($pc -lt $entryAddress -or $pc -ge $codeEndAddress) {
- throw ("MMU store to `${0:X4} from `${1:X4}, outside probe code" -f $target, $pc)
- }
- $written[$target] = $true
- $storeCount++
- }
- if (-not $windowClosed) { throw "trace has no complete entry..done probe window" }
- foreach ($register in @(0xFF00, 0xD506, 0xD507, 0xD508, 0xD509, 0xD50A)) {
- if (-not $written.ContainsKey($register)) {
- throw ("probe window has no store to `${0:X4}" -f $register)
- }
- }
- foreach ($register in @(0xD500, 0xD501, 0xD502, 0xD503, 0xD504, 0xD505, 0xD50B)) {
- if ($written.ContainsKey($register)) {
- throw ("probe wrote unintended MMU register `${0:X4}" -f $register)
- }
- }
- Write-Host ("verified {0} MMU stores in the probe window, all from probe code" -f $storeCount)
-
- Write-Host ("verified MMU result `$FF, completion `$A5, banks `$11/`$22, " +
- "pages `$77/`$33, common-priority bank0 `$66/`$5A")
- Write-Host ("monitor log: {0}" -f $monitorLog)
- } finally {
- if ($client) { $client.Dispose() }
- if ($process -and -not $process.HasExited) {
- $process.Kill()
- $process.WaitForExit()
- }
- }
|