Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

198 wiersze
7.7KB

  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. [string]$Keys = "",
  123. [int]$KeyDelay = 300,
  124. [switch]$Warp,
  125. [switch]$EightyColumn,
  126. [switch]$ProgramOnDisk
  127. )
  128. $tool = Get-Nova128ViceTool $Emulator
  129. $workingDirectory = $null
  130. $autostart = $Program
  131. if ($ProgramOnDisk) {
  132. # The editor takes its write device from the disk it was loaded from.
  133. # Autostarting the copy on the session disk is what makes its saves
  134. # reach the image; a host-injected program has no such device.
  135. if (-not $DiskImage) { throw "a disk-resident program needs -DiskImage" }
  136. $autostart = [IO.Path]::GetFileName($DiskImage) + ":" + $Program
  137. $workingDirectory = Split-Path ([IO.Path]::GetFullPath($DiskImage)) -Parent
  138. } elseif (-not (Test-Path -LiteralPath $Program)) {
  139. throw "program not found: $Program"
  140. }
  141. $arguments = @()
  142. if ($DiskImage) {
  143. if (-not (Test-Path -LiteralPath $DiskImage)) {
  144. throw "disk image not found: $DiskImage"
  145. }
  146. $arguments += @("-drive8type", "1581", "-drive8truedrive", "-8",
  147. ('"' + $DiskImage + '"'))
  148. }
  149. if ($EightyColumn) { $arguments += "-80col" }
  150. $arguments += @("-autostart", ('"' + $autostart + '"'), "-autostart-warp",
  151. "-sounddev", "dummy")
  152. if ($Keys) {
  153. # VICE keybuf uses a literal backslash-n for RETURN. The delay is in
  154. # milliseconds and keeps the keys out of the autostart load.
  155. $arguments += @("-keybuf", ('"' + $Keys + '"'), "-keybuf-delay", "$KeyDelay")
  156. }
  157. if ($Warp) { $arguments += "-warp" }
  158. if ($LimitCycles -gt 0) { $arguments += @("-limitcycles", "$LimitCycles") }
  159. if ($Screenshot) {
  160. if (Test-Path -LiteralPath $Screenshot) {
  161. Remove-Item -LiteralPath $Screenshot -Force
  162. }
  163. # x64sc's plain -exitscreenshot is the VIC-II screen. On x128 the
  164. # 80-column editor draws on the VDC, which only the plain option
  165. # captures; -exitscreenshotvicii would save a blank 40-column screen.
  166. $shotOption = if ($Emulator -eq "x64sc" -or $EightyColumn) {
  167. "-exitscreenshot"
  168. } else {
  169. "-exitscreenshotvicii"
  170. }
  171. $arguments += @($shotOption, ('"' + $Screenshot + '"'))
  172. }
  173. Write-Host ("launching {0} with {1}" -f $Emulator, $autostart)
  174. $startArgs = @{ FilePath = $tool; ArgumentList = $arguments; PassThru = $true; Wait = $true }
  175. if ($workingDirectory) { $startArgs.WorkingDirectory = $workingDirectory }
  176. $process = Start-Process @startArgs
  177. if ($Screenshot -and -not (Test-Path -LiteralPath $Screenshot)) {
  178. throw "$Emulator did not create the exit screenshot $Screenshot"
  179. }
  180. if ($DiskImage) { Wait-Nova128ImageFlush -Image $DiskImage }
  181. return $process.ExitCode
  182. }
  183. Export-ModuleMember -Function Get-Nova128ViceTool, Enter-Nova128ImageSession,
  184. Exit-Nova128ImageSession, Wait-Nova128ImageFlush, Add-Nova128DiskFile,
  185. Invoke-Nova128Emulator

Powered by TurnKey Linux.