Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

191 řádky
7.4KB

  1. # Shared helpers for the sequential Nova128 editor/compiler launchers (E07-S02).
  2. # Import-Module (Join-Path $PSScriptRoot "lib\Nova128Launcher.psm1") -Force
  3. $script:ViceBin = "C:\Program Files\GTK3VICE-3.10-win64\bin"
  4. function Get-Nova128ViceTool([string]$Name) {
  5. $path = Join-Path $script:ViceBin "$Name.exe"
  6. if (-not (Test-Path -LiteralPath $path)) { throw "$Name.exe not found at $path" }
  7. return $path
  8. }
  9. # One writable session per image directory. The lock holds the owning PID and
  10. # kind; a lock whose process is gone is stale and is replaced.
  11. function Enter-Nova128ImageSession {
  12. param(
  13. [Parameter(Mandatory)][string]$Image,
  14. [Parameter(Mandatory)][ValidateSet("editor", "compiler")][string]$Kind
  15. )
  16. $folder = Split-Path ([IO.Path]::GetFullPath($Image)) -Parent
  17. New-Item -ItemType Directory -Force -Path $folder | Out-Null
  18. $lockPath = Join-Path $folder ".newlang-session.lock"
  19. if (Test-Path -LiteralPath $lockPath) {
  20. # A live holder keeps the file open without sharing, so even reading it
  21. # can fail. That is the same answer as a readable live PID.
  22. $text = ""
  23. try {
  24. $text = Get-Content -Raw -LiteralPath $lockPath
  25. } catch {
  26. throw "another Nova128 session already holds $Image"
  27. }
  28. $owner = 0
  29. $ownerKind = "another"
  30. if ($text -match '(?m)^(\d+)\s+(\S+)') {
  31. $owner = [int]$Matches[1]
  32. $ownerKind = $Matches[2]
  33. }
  34. $alive = $owner -gt 0 -and $null -ne (Get-Process -Id $owner -ErrorAction SilentlyContinue)
  35. if ($alive) {
  36. throw "the $ownerKind session (pid $owner) still owns $Image; close it before starting another"
  37. }
  38. Remove-Item -LiteralPath $lockPath -Force
  39. }
  40. try {
  41. $stream = [IO.File]::Open($lockPath, [IO.FileMode]::CreateNew,
  42. [IO.FileAccess]::Write, [IO.FileShare]::None)
  43. } catch {
  44. throw "another Nova128 session already holds $Image"
  45. }
  46. $writer = [IO.StreamWriter]::new($stream)
  47. $writer.WriteLine("$PID $Kind")
  48. $writer.Flush()
  49. return [pscustomobject]@{ Path = $lockPath; Stream = $stream; Writer = $writer
  50. Image = $Image; Kind = $Kind }
  51. }
  52. function Exit-Nova128ImageSession {
  53. param([Parameter(Mandatory)]$Session)
  54. try {
  55. $Session.Writer.Dispose()
  56. $Session.Stream.Dispose()
  57. } finally {
  58. if (Test-Path -LiteralPath $Session.Path) {
  59. Remove-Item -LiteralPath $Session.Path -Force
  60. }
  61. if ($Session.Image) {
  62. Wait-Nova128ImageFlush -Image $Session.Image
  63. }
  64. }
  65. }
  66. # The emulator has exited, but DOS writes may still be in flight. Return only
  67. # when the image can be opened exclusively.
  68. function Wait-Nova128ImageFlush {
  69. param(
  70. [Parameter(Mandatory)][string]$Image,
  71. [int]$TimeoutSeconds = 30
  72. )
  73. if (-not (Test-Path -LiteralPath $Image)) { return }
  74. $deadline = [DateTime]::UtcNow.AddSeconds($TimeoutSeconds)
  75. while ($true) {
  76. try {
  77. $stream = [IO.File]::Open($Image, [IO.FileMode]::Open,
  78. [IO.FileAccess]::Read, [IO.FileShare]::None)
  79. $stream.Dispose()
  80. return
  81. } catch {
  82. if ([DateTime]::UtcNow -ge $deadline) {
  83. throw "the emulator exited but $Image is still locked (image not flushed)"
  84. }
  85. Start-Sleep -Milliseconds 200
  86. }
  87. }
  88. }
  89. # Start an emulator and wait for it to exit. Autostart uses the host program
  90. # path, so the program is deterministic; the disk image stays attached for the
  91. # session's own reads and writes.
  92. # Write one file into a disk image and prove it appears in the directory.
  93. function Add-Nova128DiskFile {
  94. param(
  95. [Parameter(Mandatory)][string]$Image,
  96. [Parameter(Mandatory)][string]$HostPath,
  97. [Parameter(Mandatory)][string]$DiskName,
  98. [ValidateSet("prg", "seq")][string]$Type = "prg"
  99. )
  100. $c1541 = Get-Nova128ViceTool "c1541"
  101. if (-not (Test-Path -LiteralPath $HostPath -PathType Leaf)) {
  102. throw "file not found: $HostPath"
  103. }
  104. $destination = if ($Type -eq "seq") { "$DiskName,s" } else { $DiskName }
  105. $output = (& $c1541 $Image -write $HostPath $destination 2>&1) -join "`n"
  106. if ($LASTEXITCODE -ne 0) {
  107. throw "c1541 could not write '$DiskName' to $Image`n$output"
  108. }
  109. $listing = (& $c1541 $Image -list 2>&1) -join "`n"
  110. $pattern = '(?im)"' + [regex]::Escape($DiskName) + '"\s+' + $Type
  111. if ($listing -notmatch $pattern) {
  112. throw "$DiskName ($Type) is missing from $Image after writing"
  113. }
  114. }
  115. function Invoke-Nova128Emulator {
  116. param(
  117. [Parameter(Mandatory)][ValidateSet("x64sc", "x128")][string]$Emulator,
  118. [Parameter(Mandatory)][string]$Program,
  119. [string]$DiskImage,
  120. [string]$Screenshot,
  121. [int]$LimitCycles = 0,
  122. [switch]$Warp,
  123. [switch]$EightyColumn,
  124. [switch]$ProgramOnDisk
  125. )
  126. $tool = Get-Nova128ViceTool $Emulator
  127. $workingDirectory = $null
  128. $autostart = $Program
  129. if ($ProgramOnDisk) {
  130. # The editor takes its write device from the disk it was loaded from.
  131. # Autostarting the copy on the session disk is what makes its saves
  132. # reach the image; a host-injected program has no such device.
  133. if (-not $DiskImage) { throw "a disk-resident program needs -DiskImage" }
  134. $autostart = [IO.Path]::GetFileName($DiskImage) + ":" + $Program
  135. $workingDirectory = Split-Path ([IO.Path]::GetFullPath($DiskImage)) -Parent
  136. } elseif (-not (Test-Path -LiteralPath $Program)) {
  137. throw "program not found: $Program"
  138. }
  139. $arguments = @()
  140. if ($DiskImage) {
  141. if (-not (Test-Path -LiteralPath $DiskImage)) {
  142. throw "disk image not found: $DiskImage"
  143. }
  144. $arguments += @("-drive8type", "1581", "-drive8truedrive", "-8",
  145. ('"' + $DiskImage + '"'))
  146. }
  147. if ($EightyColumn) { $arguments += "-80col" }
  148. $arguments += @("-autostart", ('"' + $autostart + '"'), "-autostart-warp",
  149. "-sounddev", "dummy")
  150. if ($Warp) { $arguments += "-warp" }
  151. if ($LimitCycles -gt 0) { $arguments += @("-limitcycles", "$LimitCycles") }
  152. if ($Screenshot) {
  153. if (Test-Path -LiteralPath $Screenshot) {
  154. Remove-Item -LiteralPath $Screenshot -Force
  155. }
  156. # x64sc's plain -exitscreenshot is the VIC-II screen. On x128 the
  157. # 80-column editor draws on the VDC, which only the plain option
  158. # captures; -exitscreenshotvicii would save a blank 40-column screen.
  159. $shotOption = if ($Emulator -eq "x64sc" -or $EightyColumn) {
  160. "-exitscreenshot"
  161. } else {
  162. "-exitscreenshotvicii"
  163. }
  164. $arguments += @($shotOption, ('"' + $Screenshot + '"'))
  165. }
  166. Write-Host ("launching {0} with {1}" -f $Emulator, $autostart)
  167. $startArgs = @{ FilePath = $tool; ArgumentList = $arguments; PassThru = $true; Wait = $true }
  168. if ($workingDirectory) { $startArgs.WorkingDirectory = $workingDirectory }
  169. $process = Start-Process @startArgs
  170. if ($Screenshot -and -not (Test-Path -LiteralPath $Screenshot)) {
  171. throw "$Emulator did not create the exit screenshot $Screenshot"
  172. }
  173. if ($DiskImage) { Wait-Nova128ImageFlush -Image $DiskImage }
  174. return $process.ExitCode
  175. }
  176. Export-ModuleMember -Function Get-Nova128ViceTool, Enter-Nova128ImageSession,
  177. Exit-Nova128ImageSession, Wait-Nova128ImageFlush, Add-Nova128DiskFile,
  178. Invoke-Nova128Emulator

Powered by TurnKey Linux.