# E01-S04: build and verify display/clock behavior in both x128 modes. [CmdletBinding()] param( [int]$RemotePort = 6510, [int]$LimitCycles = 30000000 ) $ErrorActionPreference = "Stop" $project = Split-Path $PSScriptRoot -Parent $build = Join-Path $project "build" $program = Join-Path $build "display-clock.prg" $traceCommands = Join-Path $project "monitor\display-clock-trace.txt" $x128 = "C:\Program Files\GTK3VICE-3.10-win64\bin\x128.exe" & (Join-Path $PSScriptRoot "build-display-clock.ps1") if ($LASTEXITCODE -ne 0) { throw "display/clock build failed" } if (-not (Test-Path -LiteralPath $x128)) { throw "x128.exe not found at $x128" } function Quote-Arg([string]$value) { if ($value -ne "" -and $value -notmatch '[\s"]') { return $value } $escaped = $value -replace '(\\*)"', '$1$1\"' $escaped = $escaped -replace '(\\+)$', '$1$1' return '"' + $escaped + '"' } function Start-X128([string[]]$arguments) { $psi = [Diagnostics.ProcessStartInfo]::new() $psi.FileName = $x128 $psi.Arguments = ($arguments | ForEach-Object { Quote-Arg $_ }) -join ' ' $psi.UseShellExecute = $true $psi.WindowStyle = [Diagnostics.ProcessWindowStyle]::Hidden return [Diagnostics.Process]::Start($psi) } function Invoke-Screenshot([string]$mode) { $screenshot = Join-Path $build ("display-clock-{0}.png" -f $mode) if (Test-Path -LiteralPath $screenshot) { Remove-Item -LiteralPath $screenshot -Force } $captureOption = if ($mode -eq '40') { '-exitscreenshotvicii' } else { '-exitscreenshot' } $process = Start-X128 @( ("-{0}col" -f $mode), '-autostart', $program, '-autostart-warp', '-warp', '-sounddev', 'dummy', '-limitcycles', "$LimitCycles", $captureOption, $screenshot ) $process.WaitForExit() if (-not (Test-Path -LiteralPath $screenshot)) { throw "x128 did not create $screenshot" } if ((Get-Item -LiteralPath $screenshot).Length -lt 1000) { throw "$screenshot is unexpectedly small" } Write-Host ("{0}-column screenshot: {1}" -f $mode, $screenshot) } function Invoke-MonitorVerification( [string]$mode, [byte]$expectedResult, [byte]$expectedMode, [byte]$expectedFast ) { $transcript = Join-Path $build ("display-clock-{0}-remote.txt" -f $mode) foreach ($path in @($transcript)) { if (Test-Path -LiteralPath $path) { Remove-Item -LiteralPath $path -Force } } $process = Start-X128 @( ("-{0}col" -f $mode), '-autostart', $program, '-autostart-warp', '-warp', '-sounddev', 'dummy', '-moncommands', $traceCommands, '-remotemonitor', '-remotemonitoraddress', ("ip4://127.0.0.1:{0}" -f $RemotePort) ) $client = $null try { $deadline = [DateTime]::UtcNow.AddSeconds(10) while ([DateTime]::UtcNow -lt $deadline) { if ($process.HasExited) { throw "x128 exited before $mode-column 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 the 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 $1d0f $1d14') Start-Sleep -Milliseconds 300 $writer.WriteLine('m $d030 $d030') Start-Sleep -Milliseconds 700 $buffer = New-Object byte[] 8192 $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 } [IO.File]::WriteAllText($transcript, $response, [Text.Encoding]::ASCII) $expectedBytes = '{0:x2}\s+{1:x2}\s+{2:x2}\s+00\s+5a\s+c3' -f $expectedResult, $expectedMode, $expectedFast if ($response -notmatch ("(?i)C:1d0f\s+" + $expectedBytes)) { throw "$mode-column result block mismatch; response: $response" } $clockMatch = [regex]::Match($response, '(?i)C:d030\s+([0-9a-f]{2})\b') if (-not $clockMatch.Success) { throw "$mode-column response did not include `$D030" } $clockValue = [Convert]::ToByte($clockMatch.Groups[1].Value, 16) if (($clockValue -band 0x01) -ne 0) { throw "$mode-column fallback left `$D030 speed bit set" } $writer.WriteLine('q') $writer.Dispose() $client.Dispose() $client = $null if (-not $process.WaitForExit(5000)) { throw "x128 did not exit after remote-monitor quit" } # With a remote monitor connected, non-stopping trace messages are # delivered in the remote transcript. $trace = $response if ($trace -notmatch '(?i)1c8c') { throw "$mode-column trace did not reach fallback_done" } if ($mode -eq '40') { if ($trace -notmatch '(?i)1c2e') { throw '40-column trace missed its path' } if ($trace -match '(?i)1c49') { throw '40-column path entered fast checkpoint' } } else { if ($trace -notmatch '(?i)1c3d') { throw '80-column trace missed its path' } if ($trace -notmatch '(?i)1c49') { throw '80-column trace missed fast checkpoint' } } Write-Host ("verified {0}-column result `${1:X2}, mode `${2:X2}, fast `${3:X2}, final clock `$00" -f $mode, $expectedResult, $expectedMode, $expectedFast) } finally { if ($client) { $client.Dispose() } if ($process -and -not $process.HasExited) { $process.Kill() $process.WaitForExit() } } } Invoke-Screenshot '40' Invoke-Screenshot '80' Invoke-MonitorVerification '40' 0x44 0x00 0x00 Invoke-MonitorVerification '80' 0x85 0x80 0x01 Write-Host 'display/clock probe passed in 40- and 80-column modes'