|
- # 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()
- }
- }
|