|
- # 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'
|