# E01-S05: link the memory-map skeleton against cfg/compiler.cfg. # build-map-skeleton.ps1 normal build, prints segment usage # build-map-skeleton.ps1 -Overflow ZP build that must fail in ld65 [CmdletBinding()] param( [ValidateSet("", "ZP", "COMMON", "MAIN", "SWSTACK", "TABLES")] [string]$Overflow = "" ) $ErrorActionPreference = "Stop" $project = Split-Path $PSScriptRoot -Parent $build = Join-Path $project "build" $config = Join-Path $project "cfg\compiler.cfg" $sources = @( (Join-Path $project "src\compiler\mmu.s"), (Join-Path $project "tests\platform\map-skeleton.s") ) $suffix = if ($Overflow) { "-overflow-" + $Overflow.ToLowerInvariant() } else { "" } $stem = Join-Path $build ("map-skeleton" + $suffix) $program = "$stem.prg" $map = "$stem.map" $labels = "$stem.lbl" $ca65 = (Get-Command ca65 -ErrorAction Stop).Source $ld65 = (Get-Command ld65 -ErrorAction Stop).Source New-Item -ItemType Directory -Force -Path $build | Out-Null foreach ($path in @($program, $map, $labels)) { if (Test-Path -LiteralPath $path) { Remove-Item -LiteralPath $path -Force } } $objects = @() foreach ($source in $sources) { $object = Join-Path $build (([IO.Path]::GetFileNameWithoutExtension($source)) + $suffix + ".o") $defines = @() if ($Overflow) { $defines = @("-D", "OVERFLOW_$Overflow=1") } & $ca65 --cpu 6502 @defines -o $object $source if ($LASTEXITCODE -ne 0) { throw "ca65 failed on $source" } $objects += $object } $linkOutput = & $ld65 -C $config -o $program -m $map -Ln $labels @objects 2>&1 $linkExit = $LASTEXITCODE if ($Overflow) { $text = ($linkOutput | Out-String) if ($linkExit -eq 0) { throw "OVERFLOW_$Overflow linked; capacity check failed to fire" } if ($text -notmatch "(?i)overflows memory area") { throw "OVERFLOW_$Overflow failed for an unexpected reason: $text" } $area = [regex]::Match($text, "(?i)overflows memory area\W+(\w+)").Groups[1].Value Write-Host ("overflow {0}: ld65 rejected it (memory area {1})" -f $Overflow, $area) return } if ($linkExit -ne 0) { throw ("ld65 failed:`n" + ($linkOutput | Out-String)) } # Segment usage from the map file: name, start, end, size. $segments = @{} $mapText = Get-Content -Raw -LiteralPath $map $block = [regex]::Match($mapText, "(?s)Segment list:.*?-{5,}\r?\n(.*?)\r?\n\r?\n").Groups[1].Value foreach ($line in ($block -split "\r?\n")) { $m = [regex]::Match($line, "^(\w+)\s+([0-9A-F]{6})\s+([0-9A-F]{6})\s+([0-9A-F]{6})") if ($m.Success) { $segments[$m.Groups[1].Value] = @{ Start = [Convert]::ToInt32($m.Groups[2].Value, 16) End = [Convert]::ToInt32($m.Groups[3].Value, 16) Size = [Convert]::ToInt32($m.Groups[4].Value, 16) } } } # Contract checks that ld65 cannot express: fixed placements. function Require-Segment([string]$name, [int]$lo, [int]$hi) { if (-not $segments.ContainsKey($name)) { throw "segment $name missing from map" } $seg = $segments[$name] if ($seg.Size -gt 0 -and ($seg.Start -lt $lo -or $seg.End -gt $hi)) { throw ("segment {0} `${1:X4}-`${2:X4} outside `${3:X4}-`${4:X4}" -f $name, $seg.Start, $seg.End, $lo, $hi) } } Require-Segment "ZEROPAGE" 0x0002 0x00FF Require-Segment "COMMON" 0x1300 0x18FF Require-Segment "MAILBOX" 0x1BF0 0x1BFF Require-Segment "STARTUP" 0x1C0D 0xB7FF Require-Segment "CODE" 0x1C0D 0xB7FF if ($segments["STARTUP"].Start -ne 0x1C0D) { throw "STARTUP does not begin at `$1C0D" } $labelText = Get-Content -Raw -LiteralPath $labels if ($labelText -notmatch "(?im)^al\s+001C0D\s+\.skel_entry\s*$") { throw "skel_entry is not at `$1C0D (the SYS 7181 target)" } Require-Segment "SWSTACK" 0xB800 0xBFFF Require-Segment "TABLES" 0x6B00 0xBFFF if ($segments["MAILBOX"].Start -ne 0x1BF0) { throw "mailbox is not at `$1BF0" } $bytes = [IO.File]::ReadAllBytes($program) if ($bytes[0] -ne 0x01 -or $bytes[1] -ne 0x1C) { throw "load address is not `$1C01" } Write-Host ("built {0} ({1} bytes)" -f $program, $bytes.Length) $limits = [ordered]@{ ZEROPAGE = 0x00FE; COMMON = 0x0600; SWSTACK = 0x0800; TABLES = 0x5500 } foreach ($name in ($segments.Keys | Sort-Object { $segments[$_].Start })) { $seg = $segments[$name] $limit = if ($limits.Contains($name)) { " of {0}" -f $limits[$name] } else { "" } Write-Host (" {0,-9} `${1:X4}-`${2:X4} {3,6} bytes{4}" -f $name, $seg.Start, $seg.End, $seg.Size, $limit) }