|
- # E07-S03: create a Nova128 source in the Vim clone, prove the bytes on the disk
- # are the expected PETSCII/CR file, reopen it in a second Vim session, and
- # tokenize the disk file on C128 without a host conversion.
- [CmdletBinding()]
- param([int]$Port = 6541, [int]$SessionTimeoutSeconds = 300, [int]$MailboxSeconds = 180)
-
- $ErrorActionPreference = "Stop"
-
- $project = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
- $repository = Split-Path $project -Parent
- Import-Module (Join-Path $project "scripts\lib\Nova128Test.psm1") -Force
- . (Join-Path $project "tests\lexer\reference-lexer.ps1")
-
- $root = Join-Path $project "build\vim-roundtrip"
- New-Item -ItemType Directory -Force -Path $root | Out-Null
- & (Join-Path $project "scripts\build-tokenize-seq.ps1") | Out-Host
-
- $x128 = Get-ViceTool "x128"
- $c1541 = Get-ViceTool "c1541"
- $sourceImage = Join-Path $repository "vim\vim-improved.d81"
- $editorPrg = Join-Path $repository "vim\vim128.prg"
- $work = Join-Path $root "work.d81"
- $roundtrip = Join-Path $root "roundtrip.pet"
- $copy = Join-Path $root "copy.pet"
-
- if (-not (Test-Path -LiteralPath $sourceImage -PathType Leaf)) {
- throw "editor image not found: $sourceImage"
- }
- if (-not (Test-Path -LiteralPath $editorPrg -PathType Leaf)) {
- throw "C128 editor program not found: $editorPrg"
- }
- $sourceHashBefore = (Get-FileHash -LiteralPath $sourceImage -Algorithm SHA256).Hash
- Copy-Item -LiteralPath $sourceImage -Destination $work -Force
- # The session's disk carries the C128 editor as well, so the machine can
- # reload it without the host.
- $injected = (& $c1541 $work -write $editorPrg "vim128" 2>&1) -join "`n"
- if ($LASTEXITCODE -ne 0) { throw "could not inject vim128 into the session disk:`n$injected" }
-
- # Both PETSCII letter ranges appear: lowercase identifiers and uppercase ones.
- $text = @(
- "program RoundTrip",
- "var alpha as byte",
- "var BETA as byte",
- "alpha = 1",
- "BETA = 2",
- "print alpha",
- "print BETA",
- "end program") -join "`n"
-
- # VICE keybuf uses a literal backslash-n for RETURN; \x03 is RUN/STOP, which
- # leaves the editor's insert mode.
- $typed = $text -split "`n"
- $insert = ($typed -join "\n")
-
- function Invoke-EditorSession {
- param(
- [Parameter(Mandatory = $true)][string]$Keys,
- [Parameter(Mandatory = $true)][string]$Screenshot
- )
-
- if (Test-Path -LiteralPath $Screenshot) { Remove-Item -LiteralPath $Screenshot -Force }
- $psi = [Diagnostics.ProcessStartInfo]::new()
- $psi.FileName = $x128
- # The C128 editor is an 80-column program, so the process starts in 80col
- # mode and the exit screenshot must be the plain option (VDC screen). It
- # is autostarted from the session disk: a host-loaded program gets no
- # device number, and vim128 then reports `written` without touching the
- # image. The machine runs at 1 MHz apart from autostart, so the wall-clock
- # key delay is also a cycle delay.
- $psi.WorkingDirectory = $root
- $psi.Arguments = @(
- "-80col",
- "-autostart", '"work.d81:vim128"',
- # A D81 needs a 1581 as drive 8, otherwise :wq fails with "cannot write".
- "-drive8type", "1581",
- "-8", "work.d81",
- "-autostart-warp",
- "-keybuf", ('"' + $Keys + '"'),
- "-keybuf-delay", "300",
- "-limitcycles", "120000000",
- "-exitscreenshot", ('"' + $Screenshot + '"'),
- "-sounddev", "dummy"
- ) -join ' '
- $psi.UseShellExecute = $false
- $psi.CreateNoWindow = $true
- $process = [Diagnostics.Process]::Start($psi)
- if (-not $process.WaitForExit($SessionTimeoutSeconds * 1000)) {
- $process.Kill()
- throw "x64sc session exceeded $SessionTimeoutSeconds s"
- }
- if (-not (Test-Path -LiteralPath $Screenshot)) {
- throw "x64sc produced no exit screenshot; the session did not run"
- }
- }
-
- function Read-DiskSeq {
- param(
- [Parameter(Mandatory = $true)][string]$DiskName,
- [Parameter(Mandatory = $true)][string]$HostPath
- )
- if (Test-Path -LiteralPath $HostPath) { Remove-Item -LiteralPath $HostPath -Force }
- $output = (& $c1541 $work -read "$DiskName,s" $HostPath 2>&1) -join "`n"
- if (-not (Test-Path -LiteralPath $HostPath -PathType Leaf)) {
- throw "could not read $DiskName from the session disk:`n$output"
- }
- return ,([IO.File]::ReadAllBytes($HostPath))
- }
-
- function Compare-Bytes {
- param(
- [Parameter(Mandatory = $true)][byte[]]$Actual,
- [Parameter(Mandatory = $true)][byte[]]$Expected,
- [Parameter(Mandatory = $true)][string]$What
- )
- $common = [Math]::Min($Actual.Length, $Expected.Length)
- for ($i = 0; $i -lt $common; $i++) {
- if ($Actual[$i] -ne $Expected[$i]) {
- throw ("{0}: byte {1} is `${2:X2}, expected `${3:X2} (actual {4} bytes, expected {5})" -f
- $What, $i, $Actual[$i], $Expected[$i], $Actual.Length, $Expected.Length)
- }
- }
- if ($Actual.Length -ne $Expected.Length) {
- throw ("{0}: length {1}, expected {2}" -f $What, $Actual.Length, $Expected.Length)
- }
- }
-
- # 1: create the file entirely in the editor. The name, the create answer, the
- # insert-mode text, RUN/STOP, and :wq all come from one keybuf string.
- Invoke-EditorSession -Keys ("roundtrip.n8\n" + "y" + "i" + $insert + "\x03:wq\n") `
- -Screenshot (Join-Path $root "edit.png")
- $written = Read-DiskSeq -DiskName "roundtrip.n8" -HostPath $roundtrip
- $expected = ConvertTo-Nova128Petscii ($text + "`n")
- Compare-Bytes -Actual $written -Expected $expected -What "editor-written ROUNDTRIP.N8"
- $hasLow = @($written | Where-Object { $_ -ge 0x41 -and $_ -le 0x5A })
- $hasHigh = @($written | Where-Object { $_ -ge 0xC1 -and $_ -le 0xDA })
- if (-not $hasLow -or -not $hasHigh) {
- throw "the written file does not exercise both PETSCII letter ranges"
- }
- Write-Host ("editor file: {0} bytes, PETSCII ``$41-``$5A and ``$C1-``$DA both present" -f $written.Length)
-
- # 2: reopen the same file in a second session and write the buffer to another
- # name, which proves the editor restored the content it had saved.
- Invoke-EditorSession -Keys ("roundtrip.n8\n" + ":w copy.n8\n" + ":q\n") `
- -Screenshot (Join-Path $root "reopen.png")
- $rewritten = Read-DiskSeq -DiskName "copy.n8" -HostPath $copy
- Compare-Bytes -Actual $rewritten -Expected $written -What "reopened COPY.N8"
- Write-Host "reopened in Vim and rewritten byte-identical"
-
- # 3: tokenize the editor-written file on C128 in x128.
- $disk = Join-Path $root "tokenize-seq.d81"
- $add = (& $c1541 $disk -write $roundtrip "roundtrip.n8,s" 2>&1) -join "`n"
- if ($LASTEXITCODE -ne 0) { throw "could not add the editor file to the test disk:`n$add" }
-
- $screenshot = Join-Path $root "tokenize.png"
- $session = Start-Nova128X128 -Autostart $disk -Drive8Image $disk -Port $Port -Screenshot $screenshot
- try {
- $mb = Wait-Nova128Mailbox $session -TimeoutSeconds $MailboxSeconds
- $address = $mb[8] + 256 * $mb[9]
- $length = $mb[10] + 256 * $mb[11]
- $dump = Read-Nova128Memory $session $address $length
- } finally {
- Stop-Nova128X128 $session
- }
- [IO.File]::WriteAllBytes((Join-Path $root "token-dump.bin"), $dump)
-
- if ($mb[3] -ne 0xA5) { throw ("C128 reported FAIL (code `${0:X2})" -f $mb[4]) }
- $entries = ConvertFrom-TokenDump $dump
- if ($entries.Count -ne 1) { throw "token dump holds $($entries.Count) fixtures, expected 1" }
- if ($entries[0].Load -ne 0) { throw "src_load returned $($entries[0].Load) for the editor file" }
- $wanted = @((Get-ReferenceTokens $written) | ForEach-Object { $_ })
- $got = @($entries[0].Tokens)
- if ($wanted.Count -ne $got.Count) {
- throw "C128 produced $($got.Count) tokens, the reference lexer $($wanted.Count)"
- }
- for ($i = 0; $i -lt $wanted.Count; $i++) {
- if ($wanted[$i] -ne $got[$i]) {
- throw ("token {0} differs: reference '{1}' vs C128 '{2}'" -f ($i + 1), $wanted[$i], $got[$i])
- }
- }
-
- $sourceHashAfter = (Get-FileHash -LiteralPath $sourceImage -Algorithm SHA256).Hash
- if ($sourceHashAfter -ne $sourceHashBefore) { throw "the Vim source image changed during the round-trip" }
-
- Write-Host ("tokenized on C128: {0} tokens, no host conversion" -f $got.Count)
- Write-Host ("source image SHA-256 unchanged: {0}" -f $sourceHashAfter)
- Write-Host "vim source round-trip passed"
|