# E07-S01: create a disposable Nova128 development D81 from the Vim work disk. [CmdletBinding()] param( [string]$SourceImage = "", [string]$OutputImage = "", [string]$C1541Path = "", [string]$CompilerPrg = "", [string]$ProbePrg = "", [string]$ExamplesDirectory = "", [switch]$RequireCompiler ) $ErrorActionPreference = "Stop" $project = Split-Path $PSScriptRoot -Parent $repository = Split-Path $project -Parent $build = Join-Path $project "build" if ($SourceImage -eq "") { $SourceImage = Join-Path $repository "vim\work.d81" } if ($OutputImage -eq "") { $OutputImage = Join-Path $build "newlang-dev.d81" } if ($C1541Path -eq "") { $C1541Path = "C:\Program Files\GTK3VICE-3.10-win64\bin\c1541.exe" } if ($CompilerPrg -eq "") { $CompilerPrg = Join-Path $build "n128c.prg" } if ($ProbePrg -eq "") { $ProbePrg = Join-Path $build "minimal.prg" } if ($ExamplesDirectory -eq "") { $ExamplesDirectory = Join-Path $project "examples" } $SourceImage = [IO.Path]::GetFullPath($SourceImage) $OutputImage = [IO.Path]::GetFullPath($OutputImage) $C1541Path = [IO.Path]::GetFullPath($C1541Path) $CompilerPrg = [IO.Path]::GetFullPath($CompilerPrg) $ProbePrg = [IO.Path]::GetFullPath($ProbePrg) $ExamplesDirectory = [IO.Path]::GetFullPath($ExamplesDirectory) if (-not (Test-Path -LiteralPath $SourceImage -PathType Leaf)) { throw "source D81 not found: $SourceImage" } if (-not (Test-Path -LiteralPath $C1541Path -PathType Leaf)) { throw "c1541 tool not found: $C1541Path" } if ([IO.Path]::GetExtension($OutputImage) -ine ".d81") { throw "output image must use the .d81 extension: $OutputImage" } if ([string]::Equals($SourceImage, $OutputImage, [StringComparison]::OrdinalIgnoreCase)) { throw "output image must not be the source D81" } $sourceHashBefore = (Get-FileHash -LiteralPath $SourceImage -Algorithm SHA256).Hash $outputDirectory = Split-Path $OutputImage -Parent New-Item -ItemType Directory -Force -Path $outputDirectory | Out-Null # Only the exact generated output is removed. The source image and its folder # are never used as deletion targets. if (Test-Path -LiteralPath $OutputImage) { Remove-Item -LiteralPath $OutputImage -Force } Copy-Item -LiteralPath $SourceImage -Destination $OutputImage $staging = Join-Path $build "disk-staging" New-Item -ItemType Directory -Force -Path $staging | Out-Null $injected = New-Object System.Collections.Generic.List[string] function Get-DiskDirectory { $listing = (& $C1541Path $OutputImage -list 2>&1) -join "`n" if ($LASTEXITCODE -ne 0) { throw "c1541 could not list the copied D81:`n$listing" } return $listing } function Add-DiskFile { param( [Parameter(Mandatory = $true)][string]$HostPath, [Parameter(Mandatory = $true)][string]$DiskName, [Parameter(Mandatory = $true)][ValidateSet("prg", "seq")][string]$Type ) if (-not (Test-Path -LiteralPath $HostPath -PathType Leaf)) { throw "artifact disappeared before injection: $HostPath" } $destination = if ($Type -eq "seq") { "$DiskName,s" } else { $DiskName } $writeOutput = (& $C1541Path $OutputImage -write $HostPath $destination 2>&1) -join "`n" if ($LASTEXITCODE -ne 0) { throw "c1541 failed to inject '$DiskName':`n$writeOutput" } $listing = Get-DiskDirectory $pattern = '(?im)"' + [regex]::Escape($DiskName) + '"\s+' + $Type + '\s*$' if ($listing -notmatch $pattern) { throw "c1541 reported success but '$DiskName' ($Type) is absent from the D81" } $injected.Add("$DiskName ($Type)") } function ConvertTo-PetsciiSeq { param( [Parameter(Mandatory = $true)][string]$InputPath, [Parameter(Mandatory = $true)][string]$OutputPath ) $bytes = [IO.File]::ReadAllBytes($InputPath) $start = 0 if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { $start = 3 } $converted = New-Object System.Collections.Generic.List[byte] for ($i = $start; $i -lt $bytes.Length; $i++) { $value = $bytes[$i] if ($value -eq 13) { if ($i + 1 -lt $bytes.Length -and $bytes[$i + 1] -eq 10) { continue } $converted.Add([byte]13) continue } if ($value -eq 10) { $converted.Add([byte]13) continue } if ($value -gt 0x7F) { throw "example source is not ASCII/UTF-8 text: $InputPath at byte $i" } if ($value -ge 0x61 -and $value -le 0x7A) { $converted.Add([byte]($value - 0x20)) continue } if ($value -ge 0x41 -and $value -le 0x5A) { $converted.Add([byte]($value + 0x80)) continue } $converted.Add([byte]$value) } [IO.File]::WriteAllBytes($OutputPath, $converted.ToArray()) } if (Test-Path -LiteralPath $CompilerPrg -PathType Leaf) { Add-DiskFile -HostPath $CompilerPrg -DiskName "n128c" -Type prg } elseif ($RequireCompiler) { throw "required Nova128 compiler PRG not found: $CompilerPrg" } else { Write-Warning "Nova128 compiler is not built yet; skipped $CompilerPrg" } if (Test-Path -LiteralPath $ProbePrg -PathType Leaf) { Add-DiskFile -HostPath $ProbePrg -DiskName "n128probe" -Type prg } else { Write-Warning "optional native C128 probe is absent; skipped $ProbePrg" } if (Test-Path -LiteralPath $ExamplesDirectory -PathType Container) { $examples = @(Get-ChildItem -LiteralPath $ExamplesDirectory -Filter "*.n8" -File | Sort-Object Name) foreach ($example in $examples) { if ($example.BaseName.Length -gt 12) { throw "example base name exceeds the 12-character source profile: $($example.Name)" } $diskName = $example.BaseName.ToLowerInvariant() + ".n8" $petscii = Join-Path $staging ($example.BaseName + ".pet") ConvertTo-PetsciiSeq -InputPath $example.FullName -OutputPath $petscii Add-DiskFile -HostPath $petscii -DiskName $diskName -Type seq } if ($examples.Count -eq 0) { Write-Warning "examples directory contains no .n8 files: $ExamplesDirectory" } } else { Write-Warning "examples directory does not exist yet; skipped $ExamplesDirectory" } $sourceHashAfter = (Get-FileHash -LiteralPath $SourceImage -Algorithm SHA256).Hash if ($sourceHashAfter -ne $sourceHashBefore) { throw "source D81 changed while preparing the disposable image" } # A final listing proves the resulting image is readable after all writes. $null = Get-DiskDirectory $outputHash = (Get-FileHash -LiteralPath $OutputImage -Algorithm SHA256).Hash Write-Host "prepared disposable D81: $OutputImage" Write-Host "source SHA-256 unchanged: $sourceHashAfter" if ($injected.Count -eq 0) { Write-Host "injected: no optional Nova128 artifacts are built yet" } else { Write-Host ("injected: " + ($injected -join ", ")) } [pscustomobject]@{ SourceImage = $SourceImage OutputImage = $OutputImage SourceHash = $sourceHashAfter OutputHash = $outputHash Injected = @($injected) }