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