Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

242 строки
9.4KB

  1. # build.ps1 - build a Power C program for the C64 on a fresh work disk, then
  2. # optionally leave it back in BASIC, loaded and running.
  3. #
  4. # powershell -NoProfile -File build.ps1
  5. # powershell -NoProfile -File build.ps1 -Run -Keys 'vim.c\njjj'
  6. # powershell -NoProfile -File build.ps1 -Shell 'l\n' (poke at the disk)
  7. #
  8. # Everything happens in ONE emulator session: compile each unit, link, then
  9. # "bye" out of the Power C shell, load the program from BASIC and run it. One
  10. # session is not just faster, it is required: two emulators both attached to
  11. # work.d81 write the image back over each other on exit and the files made by
  12. # the other one disappear.
  13. #
  14. # Power C is interactive - cc asks which disk to insert three times, the linker
  15. # wants one object file per ">" line, "^" for the libraries, an empty RETURN
  16. # and then a name - so every prompt is answered from the -keybuf string. VICE
  17. # paces those keystrokes in wall-clock milliseconds while the machine runs at
  18. # warp, so the delay has to be long enough that a step's answers arrive after
  19. # its prompt: cc, for one, clears the keyboard buffer when it starts.
  20. param(
  21. [string]$Prog = "vim",
  22. [string]$Keys = "",
  23. [string]$Shell = "",
  24. [int]$Delay = 900, # ms per keystroke; wall clock, machine is at warp
  25. [int]$Pad = 20, # spare RETURNs while a step is working
  26. [int]$Tail = 8, # spare RETURNs while the linker writes
  27. [int]$Settle = 0, # extra seconds to run after the last key (screen redraw)
  28. [switch]$Full, # ignore the stamps: fresh disk, recompile everything
  29. [switch]$Run
  30. )
  31. $ErrorActionPreference = "Stop"
  32. $vice = "C:\Program Files\GTK3VICE-3.10-win64\bin"
  33. $x64 = Join-Path $vice "x64sc.exe"
  34. $c1541 = Join-Path $vice "c1541.exe"
  35. $here = $PSScriptRoot
  36. $base = Join-Path $here "..\reference\POWERc.d81"
  37. $shots = Join-Path $here "shots"
  38. # VICE splits "image:file" on a colon, so keep the image name bare and run from
  39. # this folder.
  40. Set-Location $here
  41. $img = "work.d81"
  42. $units = @{
  43. vim = @("vim", "vscreen", "vview", "vedit", "vml")
  44. mltest = @("mltest", "vml")
  45. }
  46. if (-not $units.ContainsKey($Prog)) { throw "unknown program '$Prog'" }
  47. $unitList = $units[$Prog]
  48. if (-not (Test-Path $x64)) { throw "x64sc.exe not found in $vice" }
  49. if (-not (Test-Path $base)) { throw "$base not found - is the repository complete?" }
  50. New-Item -ItemType Directory -Force -Path $shots | Out-Null
  51. # ---------------------------------------------------------------- petscii
  52. # Power C sources live on the disk as PETSCII SEQ files with CR line endings:
  53. # a-z -> $41-$5A, A-Z -> $C1-$DA, CRLF -> CR, and the four characters the manual
  54. # says are typed with shift/logo keys: { | } ~ -> $DB $DC $DD $DE.
  55. function ConvertTo-Petscii([string]$in, [string]$out) {
  56. $bytes = [IO.File]::ReadAllBytes($in)
  57. $start = 0
  58. if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and
  59. $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { $start = 3 }
  60. $o = New-Object System.Collections.Generic.List[byte]
  61. for ($i = $start; $i -lt $bytes.Length; $i++) {
  62. $b = $bytes[$i]
  63. if ($b -eq 13) { continue }
  64. if ($b -eq 10) { $o.Add([byte]13); continue }
  65. if ($b -ge 0x61 -and $b -le 0x7A) { $o.Add([byte]($b - 0x20)); continue }
  66. if ($b -ge 0x41 -and $b -le 0x5A) { $o.Add([byte]($b + 0x80)); continue }
  67. if ($b -ge 0x7B -and $b -le 0x7F) { $o.Add([byte]($b + 0x60)); continue }
  68. $o.Add([byte]$b)
  69. }
  70. [IO.File]::WriteAllBytes($out, $o.ToArray())
  71. return $o.Count
  72. }
  73. # ---------------------------------------------------------------- emulator
  74. # Quote one argument the way the Windows command line wants it. Start-Process's
  75. # -ArgumentList joins with spaces and does not quote anything, which silently
  76. # split "-keybuf cc vim.c\n" into two arguments, so the command line is built
  77. # here and handed to .NET as a single string.
  78. function Quote-Arg([string]$a) {
  79. if ($a -ne "" -and $a -notmatch '[\s"]') { return $a }
  80. $e = $a -replace '(\\*)"', '$1$1\"'
  81. $e = $e -replace '(\\+)$', '$1$1'
  82. return '"' + $e + '"'
  83. }
  84. # Exactly one emulator, waited for, so nothing else touches the image.
  85. function Invoke-C64 {
  86. param(
  87. [string]$KeyBuf,
  88. [string]$Shot,
  89. [string]$Target = "shell",
  90. [int]$Cycles = 0,
  91. [int]$KeyDelay = 900
  92. )
  93. if (Test-Path $Shot) { Remove-Item $Shot -Force } # VICE won't overwrite
  94. if ($Cycles -le 0) {
  95. $wall = ($KeyBuf.Length * $KeyDelay / 1000.0) + 15 + $Settle
  96. $c = [long]($wall * 13000000)
  97. if ($c -lt 150000000) { $c = 150000000 }
  98. if ($c -gt 2000000000) { $c = 2000000000 }
  99. $Cycles = [int]$c
  100. }
  101. $argv = @(
  102. "-8", $img,
  103. "-autostart", ($img + ":" + $Target),
  104. "-autostart-warp", "-warp", "-sounddev", "dummy",
  105. "-keybuf-delay", "$KeyDelay",
  106. "-keybuf", $KeyBuf,
  107. "-limitcycles", "$Cycles",
  108. "-exitscreenshot", $Shot
  109. )
  110. $cmdline = ($argv | ForEach-Object { Quote-Arg $_ }) -join ' '
  111. $psi = New-Object System.Diagnostics.ProcessStartInfo
  112. $psi.FileName = $x64
  113. $psi.Arguments = $cmdline
  114. $psi.UseShellExecute = $false
  115. $pi = [System.Diagnostics.Process]::Start($psi)
  116. $pi.WaitForExit()
  117. Start-Sleep -Milliseconds 900 # let the image write settle
  118. }
  119. function Get-DiskDir {
  120. return (& $c1541 $img -list 2>$null) -join "`n"
  121. }
  122. # ---------------------------------------------------------------- raw shell
  123. if ($Shell -ne "") {
  124. if (-not (Test-Path $img)) { throw "$img not found - build something first" }
  125. Invoke-C64 -KeyBuf $Shell -Shot (Join-Path $shots "shell.png") -KeyDelay $Delay
  126. Write-Host "shots\shell.png"
  127. exit 0
  128. }
  129. # ---------------------------------------------------------------- fresh disk
  130. # work.stamp remembers the hash of each .c as it was last compiled onto the work
  131. # disk. Only units whose hash differs are converted, copied over and recompiled;
  132. # the others keep the .o already on the disk. The linker then runs again.
  133. $stampFile = Join-Path $here "work.stamp"
  134. $stamps = @{}
  135. if (Test-Path $stampFile) {
  136. foreach ($ln in (Get-Content $stampFile)) {
  137. $p = $ln -split '='
  138. if ($p.Count -eq 2) { $stamps[$p[0]] = $p[1] }
  139. }
  140. }
  141. if ($Full -or -not (Test-Path $img) -or -not (Test-Path $stampFile)) {
  142. Write-Host "== fresh work disk"
  143. Copy-Item $base $img -Force
  144. $stamps = @{}
  145. } else {
  146. Write-Host "== incremental build on $img"
  147. }
  148. $changed = @()
  149. foreach ($u in $unitList) {
  150. $src = Join-Path $here "$u.c"
  151. if (-not (Test-Path $src)) { throw "$src not found" }
  152. $hash = (Get-FileHash $src -Algorithm MD5).Hash
  153. if ($stamps[$u] -eq $hash) { continue }
  154. $changed += $u
  155. $pet = Join-Path $here "$u.pet"
  156. $n = ConvertTo-Petscii $src $pet
  157. Write-Host ("== {0}.c changed -> petscii ({1} bytes)" -f $u, $n)
  158. # cc and the linker refuse to overwrite, so scratch the stale copies first
  159. $null = & $c1541 $img -delete "$u.c" "$u.o" 2>$null
  160. $null = & $c1541 $img -write $pet "$u.c,s" 2>$null
  161. $stamps.Remove($u) # only re-stamped once its .o really exists
  162. }
  163. $haveProg = ((& $c1541 $img -list 2>$null) -join "`n") -match ('"' + [regex]::Escape($Prog) + '"\s+prg')
  164. $needLink = ($changed.Count -gt 0) -or (-not $haveProg)
  165. if ($needLink -and $haveProg) { $null = & $c1541 $img -delete $Prog 2>$null }
  166. if ($changed.Count -eq 0) { Write-Host "== no source changed" }
  167. # ---------------------------------------------------------------- one session
  168. # cc <unit> and RETURNs for its disk prompts, over and over, then the linker:
  169. # ">" object files, ">" up-arrow for the libraries, ">" empty to finish, the
  170. # output name. With -Run the session goes on to leave the shell with bye, load
  171. # the program from BASIC and type -Keys at whatever it puts on the screen.
  172. $kb = ""
  173. foreach ($u in $changed) {
  174. $kb += "cc $u.c\n" + ("\n" * 3) + ("\n" * $Pad)
  175. }
  176. if ($needLink) {
  177. $kb += "link -s\n"
  178. foreach ($u in $unitList) { $kb += "$u.o\n" }
  179. $kb += "^\n\n" + $Prog + "\n" + ("\n" * $Tail)
  180. }
  181. if ((-not $Run) -and ($kb -eq "")) {
  182. Write-Host "build ok - nothing to do"
  183. exit 0
  184. }
  185. if ($Run) {
  186. $kb += "bye\n" + ("\n" * 6) # let the shell hand back to BASIC
  187. $kb += 'load"' + $Prog + '",8,1' + "\n" + ("\n" * 10) # let the load finish
  188. $kb += "run\n"
  189. $kb += $Keys
  190. }
  191. $shot = if ($Run) { Join-Path $shots "run.png" } else { Join-Path $shots "build.png" }
  192. Write-Host ("== one session: cc [{0}], {1}{2}" -f ($changed -join ' '),
  193. $(if ($needLink) { 'link' } else { 'no link' }),
  194. $(if ($Run) { ', bye, load, run' } else { '' }))
  195. Invoke-C64 -KeyBuf $kb -Shot $shot -KeyDelay $Delay
  196. $dir = Get-DiskDir
  197. $found = $false
  198. foreach ($line in ($dir -split "`n")) {
  199. if ($line -match ('"' + [regex]::Escape($Prog) + '"\s+prg')) { $found = $true }
  200. if ($line -match ('"(' + ($unitList -join '|') + ')\.[co]"')) {
  201. Write-Host " $line"
  202. }
  203. }
  204. # stamp a unit only once its object file is really on the disk
  205. foreach ($u in $changed) {
  206. if ($dir -match ('"' + [regex]::Escape($u) + '\.o"')) {
  207. $stamps[$u] = (Get-FileHash (Join-Path $here "$u.c") -Algorithm MD5).Hash
  208. } else {
  209. Write-Host " $u.c produced no object - read $shot"
  210. }
  211. }
  212. ($stamps.GetEnumerator() | ForEach-Object { "{0}={1}" -f $_.Key, $_.Value }) | Set-Content $stampFile
  213. if (-not $found) { throw "'$Prog' was not created - read $shot" }
  214. if ($Run) {
  215. Write-Host (" {0}" -f (Split-Path $shot -Leaf))
  216. } else {
  217. Write-Host "build ok - 'build.ps1 -Run -Keys ...' to try it"
  218. }

Powered by TurnKey Linux.