# E01-S03: run the KERNAL wrapper probe from a true-drive D64 in VICE x128 and # verify its machine-readable result block over the remote monitor. [CmdletBinding()] param( [int]$RemotePort = 6512, [int]$TimeoutSeconds = 60 ) $ErrorActionPreference = "Stop" $project = Split-Path $PSScriptRoot -Parent $build = Join-Path $project "build" $disk = Join-Path $build "kernal-probe.d64" $labelsPath = Join-Path $build "kernal-probe.lbl" $traceCommands = Join-Path $project "monitor\kernal-probe-trace.txt" $monitorLog = Join-Path $build "kernal-probe-monitor.log" $screenshot = Join-Path $build "kernal-probe-vicii.png" $x128 = "C:\Program Files\GTK3VICE-3.10-win64\bin\x128.exe" & (Join-Path $PSScriptRoot "build-kernal-probe.ps1") if ($LASTEXITCODE -ne 0) { throw "KERNAL probe build failed" } if (-not (Test-Path -LiteralPath $x128)) { throw "x128.exe not found at $x128" } foreach ($path in @($monitorLog, $screenshot)) { if (Test-Path -LiteralPath $path) { Remove-Item -LiteralPath $path -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 "kprobe_result" $completeAddress = Get-LabelAddress "kprobe_complete" $stateAddress = Get-LabelAddress "kprobe_state_result" $failAAddress = Get-LabelAddress "kprobe_fail_a" $failPAddress = Get-LabelAddress "kprobe_fail_p" $failStAddress = Get-LabelAddress "kprobe_fail_st" $statusAddress = Get-LabelAddress "kprobe_status_buf" $statusMax = 32 $blockEnd = $statusAddress + 2 * $statusMax - 1 $arguments = @( "-drive8type", "1541", "-drive8truedrive", "-8", ('"' + $disk + '"'), "-autostart", ('"' + $disk + '"'), "-autostart-warp", "-warp", "-sounddev", "dummy", "-exitscreenshotvicii", ('"' + $screenshot + '"'), "-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 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 # True-drive loading takes several emulated seconds; poll completion. $transcript = "" $memory = @{} $deadline = [DateTime]::UtcNow.AddSeconds($TimeoutSeconds) while ($true) { Start-Sleep -Seconds 1 $writer.WriteLine(("m `${0:X4} `${1:X4}" -f $resultAddress, $blockEnd)) $chunk = Read-Available $stream $transcript += $chunk $memory = Convert-Dump $chunk if ($memory[$completeAddress] -eq 0xA5) { break } if ([DateTime]::UtcNow -ge $deadline) { throw "probe did not complete within $TimeoutSeconds s; last response: $chunk" } $writer.WriteLine("x") $transcript += Read-Available $stream } # Let the post-done summary finish printing; x128 writes the VIC-II # screenshot when the monitor quits it below. $writer.WriteLine("x") Start-Sleep -Seconds 2 $writer.WriteLine("m 0000 0000") $transcript += Read-Available $stream [IO.File]::WriteAllText((Join-Path $build "kernal-probe-remote.txt"), $transcript) $failures = @() function Expect([string]$name, [int]$address, [int]$value) { if ($memory[$address] -ne $value) { $script:failures += ("{0} at `${1:X4} = `${2:X2}, expected `${3:X2}" -f $name, $address, $memory[$address], $value) } } Expect "result" $resultAddress 0xFF Expect "complete" $completeAddress 0xA5 foreach ($state in 0, 1) { Expect "state$state result" ($stateAddress + $state) 0xFF Expect "state$state CHKIN error" ($failAAddress + $state) 0x05 if (($memory[$failPAddress + $state] -band 0x01) -ne 0x01) { $failures += "state$state CHKIN carry was clear" } if (($memory[$failStAddress + $state] -band 0x80) -ne 0x80) { $failures += ("state{0} READST `${1:X2} lacks device-not-present bit 7" -f $state, $memory[$failStAddress + $state]) } } $statusText = @() foreach ($state in 0, 1) { $chars = @() for ($i = 0; $i -lt $statusMax; $i++) { $byte = $memory[$statusAddress + $state * $statusMax + $i] if ($byte -eq 0x0D) { break } $chars += [char]$byte } $statusText += (-join $chars) } $writer.WriteLine("quit") $writer.Dispose() $client.Dispose() $client = $null if (-not $process.WaitForExit(10000)) { throw "x128 did not exit after monitor quit" } if ($failures.Count -gt 0) { throw ("KERNAL probe verification failed:`n " + ($failures -join "`n ")) } Write-Host ("verified state results `$FF/`$FF, CHKIN(30) error 5 with C=1, " + "READST `${0:X2}/`${1:X2}" -f $memory[$failStAddress], $memory[$failStAddress + 1]) Write-Host ("drive 8 status from bank 0: '{0}'" -f $statusText[0]) Write-Host ("drive 8 status from bank 1: '{0}'" -f $statusText[1]) 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() } }