@jhlagado/azm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +649 -0
- package/README.md +142 -0
- package/dist/src/analysis.d.ts +11 -0
- package/dist/src/analysis.js +41 -0
- package/dist/src/api-compile.d.ts +8 -0
- package/dist/src/api-compile.js +3 -0
- package/dist/src/api-tooling.d.ts +25 -0
- package/dist/src/api-tooling.js +21 -0
- package/dist/src/cli.d.ts +30 -0
- package/dist/src/cli.js +523 -0
- package/dist/src/compile.d.ts +10 -0
- package/dist/src/compile.js +175 -0
- package/dist/src/compileShared.d.ts +3 -0
- package/dist/src/compileShared.js +7 -0
- package/dist/src/diagnosticTypes.d.ts +77 -0
- package/dist/src/diagnosticTypes.js +53 -0
- package/dist/src/formats/index.d.ts +7 -0
- package/dist/src/formats/index.js +17 -0
- package/dist/src/formats/range.d.ts +17 -0
- package/dist/src/formats/range.js +45 -0
- package/dist/src/formats/types.d.ts +208 -0
- package/dist/src/formats/types.js +1 -0
- package/dist/src/formats/writeAsm80.d.ts +6 -0
- package/dist/src/formats/writeAsm80.js +86 -0
- package/dist/src/formats/writeBin.d.ts +7 -0
- package/dist/src/formats/writeBin.js +23 -0
- package/dist/src/formats/writeD8m.d.ts +9 -0
- package/dist/src/formats/writeD8m.js +239 -0
- package/dist/src/formats/writeHex.d.ts +9 -0
- package/dist/src/formats/writeHex.js +39 -0
- package/dist/src/formats/writeListing.d.ts +8 -0
- package/dist/src/formats/writeListing.js +83 -0
- package/dist/src/frontend/asm80/asmLine.d.ts +39 -0
- package/dist/src/frontend/asm80/asmLine.js +89 -0
- package/dist/src/frontend/asm80/parseAsmRawValues.d.ts +4 -0
- package/dist/src/frontend/asm80/parseAsmRawValues.js +94 -0
- package/dist/src/frontend/asm80/quoteScan.d.ts +10 -0
- package/dist/src/frontend/asm80/quoteScan.js +25 -0
- package/dist/src/frontend/ast.d.ts +376 -0
- package/dist/src/frontend/ast.js +1 -0
- package/dist/src/frontend/directiveAliases.d.ts +14 -0
- package/dist/src/frontend/directiveAliases.js +189 -0
- package/dist/src/frontend/grammarData.d.ts +14 -0
- package/dist/src/frontend/grammarData.js +65 -0
- package/dist/src/frontend/immExprUtils.d.ts +2 -0
- package/dist/src/frontend/immExprUtils.js +12 -0
- package/dist/src/frontend/parseAsmFlatDirectiveLine.d.ts +17 -0
- package/dist/src/frontend/parseAsmFlatDirectiveLine.js +187 -0
- package/dist/src/frontend/parseAsmInstruction.d.ts +3 -0
- package/dist/src/frontend/parseAsmInstruction.js +73 -0
- package/dist/src/frontend/parseAsmStatements.d.ts +6 -0
- package/dist/src/frontend/parseAsmStatements.js +16 -0
- package/dist/src/frontend/parseAsmStream.d.ts +10 -0
- package/dist/src/frontend/parseAsmStream.js +33 -0
- package/dist/src/frontend/parseAsmTopLevel.d.ts +18 -0
- package/dist/src/frontend/parseAsmTopLevel.js +34 -0
- package/dist/src/frontend/parseDiagnostics.d.ts +9 -0
- package/dist/src/frontend/parseDiagnostics.js +16 -0
- package/dist/src/frontend/parseEnum.d.ts +12 -0
- package/dist/src/frontend/parseEnum.js +70 -0
- package/dist/src/frontend/parseImm.d.ts +10 -0
- package/dist/src/frontend/parseImm.js +397 -0
- package/dist/src/frontend/parseLogicalLines.d.ts +11 -0
- package/dist/src/frontend/parseLogicalLines.js +94 -0
- package/dist/src/frontend/parseOp.d.ts +25 -0
- package/dist/src/frontend/parseOp.js +120 -0
- package/dist/src/frontend/parseOpHeader.d.ts +20 -0
- package/dist/src/frontend/parseOpHeader.js +32 -0
- package/dist/src/frontend/parseOperands.d.ts +4 -0
- package/dist/src/frontend/parseOperands.js +290 -0
- package/dist/src/frontend/parseParams.d.ts +6 -0
- package/dist/src/frontend/parseParams.js +62 -0
- package/dist/src/frontend/parseParserRecovery.d.ts +12 -0
- package/dist/src/frontend/parseParserRecovery.js +17 -0
- package/dist/src/frontend/parseParserShared.d.ts +2 -0
- package/dist/src/frontend/parseParserShared.js +8 -0
- package/dist/src/frontend/parseRawDataDirectiveStart.d.ts +1 -0
- package/dist/src/frontend/parseRawDataDirectiveStart.js +3 -0
- package/dist/src/frontend/parseRawDataDirectives.d.ts +7 -0
- package/dist/src/frontend/parseRawDataDirectives.js +142 -0
- package/dist/src/frontend/parseRecordFieldDecl.d.ts +34 -0
- package/dist/src/frontend/parseRecordFieldDecl.js +177 -0
- package/dist/src/frontend/parseSourceItemDispatch.d.ts +46 -0
- package/dist/src/frontend/parseSourceItemDispatch.js +53 -0
- package/dist/src/frontend/parseSourceItemTable.d.ts +16 -0
- package/dist/src/frontend/parseSourceItemTable.js +68 -0
- package/dist/src/frontend/parseTopLevelCommon.d.ts +11 -0
- package/dist/src/frontend/parseTopLevelCommon.js +50 -0
- package/dist/src/frontend/parseTypes.d.ts +24 -0
- package/dist/src/frontend/parseTypes.js +77 -0
- package/dist/src/frontend/parser.d.ts +11 -0
- package/dist/src/frontend/parser.js +88 -0
- package/dist/src/frontend/source.d.ts +28 -0
- package/dist/src/frontend/source.js +58 -0
- package/dist/src/frontend/sourceExtensions.d.ts +2 -0
- package/dist/src/frontend/sourceExtensions.js +6 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +2 -0
- package/dist/src/lintCaseStyle.d.ts +4 -0
- package/dist/src/lintCaseStyle.js +129 -0
- package/dist/src/lowering/asmDirectiveLowering.d.ts +4 -0
- package/dist/src/lowering/asmDirectiveLowering.js +229 -0
- package/dist/src/lowering/asmDirectiveTraversal.d.ts +47 -0
- package/dist/src/lowering/asmDirectiveTraversal.js +52 -0
- package/dist/src/lowering/asmEquResolution.d.ts +8 -0
- package/dist/src/lowering/asmEquResolution.js +69 -0
- package/dist/src/lowering/asmInstructionLdHelpers.d.ts +15 -0
- package/dist/src/lowering/asmInstructionLdHelpers.js +102 -0
- package/dist/src/lowering/asmInstructionLowering.d.ts +5 -0
- package/dist/src/lowering/asmInstructionLowering.js +54 -0
- package/dist/src/lowering/asmInstructionStream.d.ts +46 -0
- package/dist/src/lowering/asmInstructionStream.js +51 -0
- package/dist/src/lowering/asmLoweringBranchCall.d.ts +43 -0
- package/dist/src/lowering/asmLoweringBranchCall.js +254 -0
- package/dist/src/lowering/asmLoweringHost.d.ts +23 -0
- package/dist/src/lowering/asmLoweringHost.js +1 -0
- package/dist/src/lowering/asmLoweringLd.d.ts +28 -0
- package/dist/src/lowering/asmLoweringLd.js +144 -0
- package/dist/src/lowering/asmRangeLowering.d.ts +17 -0
- package/dist/src/lowering/asmRangeLowering.js +39 -0
- package/dist/src/lowering/asmRawDataLowering.d.ts +16 -0
- package/dist/src/lowering/asmRawDataLowering.js +209 -0
- package/dist/src/lowering/asmSourceEmitter.d.ts +4 -0
- package/dist/src/lowering/asmSourceEmitter.js +9 -0
- package/dist/src/lowering/asmSourceInstructionLowering.d.ts +4 -0
- package/dist/src/lowering/asmSourceInstructionLowering.js +14 -0
- package/dist/src/lowering/asmUtils.d.ts +13 -0
- package/dist/src/lowering/asmUtils.js +105 -0
- package/dist/src/lowering/assemblerFlowSetup.d.ts +54 -0
- package/dist/src/lowering/assemblerFlowSetup.js +128 -0
- package/dist/src/lowering/assemblerLoweringContext.d.ts +151 -0
- package/dist/src/lowering/assemblerLoweringContext.js +16 -0
- package/dist/src/lowering/assemblerLoweringContextSplit.d.ts +7 -0
- package/dist/src/lowering/assemblerLoweringContextSplit.js +75 -0
- package/dist/src/lowering/assemblerLoweringPhases.d.ts +66 -0
- package/dist/src/lowering/assemblerLoweringPhases.js +166 -0
- package/dist/src/lowering/bytePlacement.d.ts +7 -0
- package/dist/src/lowering/bytePlacement.js +37 -0
- package/dist/src/lowering/capabilities.d.ts +67 -0
- package/dist/src/lowering/capabilities.js +1 -0
- package/dist/src/lowering/eaResolution.d.ts +58 -0
- package/dist/src/lowering/eaResolution.js +159 -0
- package/dist/src/lowering/emissionCore.d.ts +17 -0
- package/dist/src/lowering/emissionCore.js +21 -0
- package/dist/src/lowering/emit.d.ts +17 -0
- package/dist/src/lowering/emit.js +46 -0
- package/dist/src/lowering/emitContextBuilder.d.ts +63 -0
- package/dist/src/lowering/emitContextBuilder.js +41 -0
- package/dist/src/lowering/emitFinalization.d.ts +61 -0
- package/dist/src/lowering/emitFinalization.js +66 -0
- package/dist/src/lowering/emitFinalizationSetup.d.ts +19 -0
- package/dist/src/lowering/emitFinalizationSetup.js +26 -0
- package/dist/src/lowering/emitPhase1BuildProgramLoweringContext.d.ts +7 -0
- package/dist/src/lowering/emitPhase1BuildProgramLoweringContext.js +119 -0
- package/dist/src/lowering/emitPhase1Helpers.d.ts +4 -0
- package/dist/src/lowering/emitPhase1Helpers.js +12 -0
- package/dist/src/lowering/emitPhase1Types.d.ts +21 -0
- package/dist/src/lowering/emitPhase1Types.js +1 -0
- package/dist/src/lowering/emitPhase1WirePipeline.d.ts +70 -0
- package/dist/src/lowering/emitPhase1WirePipeline.js +203 -0
- package/dist/src/lowering/emitPhase1Workspace.d.ts +82 -0
- package/dist/src/lowering/emitPhase1Workspace.js +55 -0
- package/dist/src/lowering/emitPipeline.d.ts +121 -0
- package/dist/src/lowering/emitPipeline.js +57 -0
- package/dist/src/lowering/emitProgramContext.d.ts +39 -0
- package/dist/src/lowering/emitProgramContext.js +29 -0
- package/dist/src/lowering/emitState.d.ts +90 -0
- package/dist/src/lowering/emitState.js +124 -0
- package/dist/src/lowering/fixupBaseResolution.d.ts +7 -0
- package/dist/src/lowering/fixupBaseResolution.js +23 -0
- package/dist/src/lowering/fixupEmission.d.ts +64 -0
- package/dist/src/lowering/fixupEmission.js +199 -0
- package/dist/src/lowering/immMath.d.ts +2 -0
- package/dist/src/lowering/immMath.js +34 -0
- package/dist/src/lowering/inputAssets.d.ts +7 -0
- package/dist/src/lowering/inputAssets.js +106 -0
- package/dist/src/lowering/ldEncoding.d.ts +15 -0
- package/dist/src/lowering/ldEncoding.js +12 -0
- package/dist/src/lowering/ldEncodingRegMemHelpers.d.ts +5 -0
- package/dist/src/lowering/ldEncodingRegMemHelpers.js +124 -0
- package/dist/src/lowering/ldFormSelection.d.ts +26 -0
- package/dist/src/lowering/ldFormSelection.js +92 -0
- package/dist/src/lowering/ldLowering.d.ts +7 -0
- package/dist/src/lowering/ldLowering.js +13 -0
- package/dist/src/lowering/loweredAsmByteEmission.d.ts +23 -0
- package/dist/src/lowering/loweredAsmByteEmission.js +185 -0
- package/dist/src/lowering/loweredAsmPlacement.d.ts +13 -0
- package/dist/src/lowering/loweredAsmPlacement.js +86 -0
- package/dist/src/lowering/loweredAsmStreamRecording.d.ts +27 -0
- package/dist/src/lowering/loweredAsmStreamRecording.js +215 -0
- package/dist/src/lowering/loweredAsmTypes.d.ts +202 -0
- package/dist/src/lowering/loweredAsmTypes.js +1 -0
- package/dist/src/lowering/loweredFormat.d.ts +3 -0
- package/dist/src/lowering/loweredFormat.js +26 -0
- package/dist/src/lowering/loweredItemSize.d.ts +4 -0
- package/dist/src/lowering/loweredItemSize.js +17 -0
- package/dist/src/lowering/loweringDiagnostics.d.ts +9 -0
- package/dist/src/lowering/loweringDiagnostics.js +55 -0
- package/dist/src/lowering/loweringTypes.d.ts +27 -0
- package/dist/src/lowering/loweringTypes.js +1 -0
- package/dist/src/lowering/opCandidateRegistry.d.ts +9 -0
- package/dist/src/lowering/opCandidateRegistry.js +9 -0
- package/dist/src/lowering/opExpansionExecution.d.ts +15 -0
- package/dist/src/lowering/opExpansionExecution.js +45 -0
- package/dist/src/lowering/opExpansionOrchestration.d.ts +15 -0
- package/dist/src/lowering/opExpansionOrchestration.js +88 -0
- package/dist/src/lowering/opExpansionStream.d.ts +12 -0
- package/dist/src/lowering/opExpansionStream.js +176 -0
- package/dist/src/lowering/opMatching.d.ts +52 -0
- package/dist/src/lowering/opMatching.js +355 -0
- package/dist/src/lowering/opSubstitution.d.ts +13 -0
- package/dist/src/lowering/opSubstitution.js +175 -0
- package/dist/src/lowering/prescanTypes.d.ts +7 -0
- package/dist/src/lowering/prescanTypes.js +1 -0
- package/dist/src/lowering/programLowering.d.ts +144 -0
- package/dist/src/lowering/programLowering.js +2 -0
- package/dist/src/lowering/programLoweringDeclarations.d.ts +5 -0
- package/dist/src/lowering/programLoweringDeclarations.js +5 -0
- package/dist/src/lowering/programLoweringFinalize.d.ts +18 -0
- package/dist/src/lowering/programLoweringFinalize.js +115 -0
- package/dist/src/lowering/programLoweringTraversal.d.ts +2 -0
- package/dist/src/lowering/programLoweringTraversal.js +93 -0
- package/dist/src/lowering/programPrescan.d.ts +3 -0
- package/dist/src/lowering/programPrescan.js +37 -0
- package/dist/src/lowering/traceFormat.d.ts +13 -0
- package/dist/src/lowering/traceFormat.js +211 -0
- package/dist/src/pathCompare.d.ts +3 -0
- package/dist/src/pathCompare.js +26 -0
- package/dist/src/pipeline.d.ts +78 -0
- package/dist/src/pipeline.js +1 -0
- package/dist/src/registerCare/analyze.d.ts +24 -0
- package/dist/src/registerCare/analyze.js +327 -0
- package/dist/src/registerCare/annotate.d.ts +11 -0
- package/dist/src/registerCare/annotate.js +76 -0
- package/dist/src/registerCare/boundaryHints.d.ts +2 -0
- package/dist/src/registerCare/boundaryHints.js +10 -0
- package/dist/src/registerCare/carriers.d.ts +4 -0
- package/dist/src/registerCare/carriers.js +78 -0
- package/dist/src/registerCare/controlFlow.d.ts +5 -0
- package/dist/src/registerCare/controlFlow.js +35 -0
- package/dist/src/registerCare/fix.d.ts +11 -0
- package/dist/src/registerCare/fix.js +119 -0
- package/dist/src/registerCare/liveness.d.ts +7 -0
- package/dist/src/registerCare/liveness.js +227 -0
- package/dist/src/registerCare/profiles.d.ts +11 -0
- package/dist/src/registerCare/profiles.js +45 -0
- package/dist/src/registerCare/programModel.d.ts +3 -0
- package/dist/src/registerCare/programModel.js +181 -0
- package/dist/src/registerCare/report.d.ts +5 -0
- package/dist/src/registerCare/report.js +139 -0
- package/dist/src/registerCare/smartComments.d.ts +5 -0
- package/dist/src/registerCare/smartComments.js +247 -0
- package/dist/src/registerCare/sourceText.d.ts +8 -0
- package/dist/src/registerCare/sourceText.js +15 -0
- package/dist/src/registerCare/summary.d.ts +3 -0
- package/dist/src/registerCare/summary.js +492 -0
- package/dist/src/registerCare/tooling.d.ts +42 -0
- package/dist/src/registerCare/tooling.js +50 -0
- package/dist/src/registerCare/types.d.ts +154 -0
- package/dist/src/registerCare/types.js +1 -0
- package/dist/src/semantics/declVisitor.d.ts +5 -0
- package/dist/src/semantics/declVisitor.js +11 -0
- package/dist/src/semantics/env.d.ts +28 -0
- package/dist/src/semantics/env.js +432 -0
- package/dist/src/semantics/layout.d.ts +21 -0
- package/dist/src/semantics/layout.js +226 -0
- package/dist/src/semantics/layoutCastFold.d.ts +22 -0
- package/dist/src/semantics/layoutCastFold.js +118 -0
- package/dist/src/semantics/semanticsDiagnostics.d.ts +2 -0
- package/dist/src/semantics/semanticsDiagnostics.js +4 -0
- package/dist/src/semantics/typeQueries.d.ts +31 -0
- package/dist/src/semantics/typeQueries.js +124 -0
- package/dist/src/sourceIncludeExpansion.d.ts +17 -0
- package/dist/src/sourceIncludeExpansion.js +124 -0
- package/dist/src/sourceIncludePaths.d.ts +1 -0
- package/dist/src/sourceIncludePaths.js +12 -0
- package/dist/src/sourceLoader.d.ts +15 -0
- package/dist/src/sourceLoader.js +118 -0
- package/dist/src/z80/effects.d.ts +3 -0
- package/dist/src/z80/effects.js +516 -0
- package/dist/src/z80/encode.d.ts +10 -0
- package/dist/src/z80/encode.js +412 -0
- package/dist/src/z80/encodeAlu.d.ts +7 -0
- package/dist/src/z80/encodeAlu.js +219 -0
- package/dist/src/z80/encodeBitOps.d.ts +7 -0
- package/dist/src/z80/encodeBitOps.js +123 -0
- package/dist/src/z80/encodeContext.d.ts +29 -0
- package/dist/src/z80/encodeContext.js +1 -0
- package/dist/src/z80/encodeControl.d.ts +26 -0
- package/dist/src/z80/encodeControl.js +180 -0
- package/dist/src/z80/encodeCoreOps.d.ts +7 -0
- package/dist/src/z80/encodeCoreOps.js +131 -0
- package/dist/src/z80/encodeIo.d.ts +9 -0
- package/dist/src/z80/encodeIo.js +128 -0
- package/dist/src/z80/encodeLd.d.ts +13 -0
- package/dist/src/z80/encodeLd.js +273 -0
- package/dist/src/z80/encoderRegistry.d.ts +13 -0
- package/dist/src/z80/encoderRegistry.js +169 -0
- package/docs/reference/cli.md +134 -0
- package/docs/reference/tooling-api.md +248 -0
- package/package.json +98 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { formatLoweredImmExpr, formatLoweredNumber } from '../lowering/loweredFormat.js';
|
|
2
|
+
const toHexByte = (value) => value.toString(16).toUpperCase().padStart(2, '0');
|
|
3
|
+
function formatEaExpr(expr) {
|
|
4
|
+
switch (expr.kind) {
|
|
5
|
+
case 'name':
|
|
6
|
+
return expr.name;
|
|
7
|
+
case 'imm':
|
|
8
|
+
return formatLoweredImmExpr(expr.expr);
|
|
9
|
+
case 'add':
|
|
10
|
+
return `${formatEaExpr(expr.base)}+${formatLoweredImmExpr(expr.offset)}`;
|
|
11
|
+
case 'sub':
|
|
12
|
+
return `${formatEaExpr(expr.base)}-${formatLoweredImmExpr(expr.offset)}`;
|
|
13
|
+
case 'field':
|
|
14
|
+
case 'index':
|
|
15
|
+
case 'layoutCast':
|
|
16
|
+
throw new Error(`ASM80 emitter cannot format lowered EA kind "${expr.kind}".`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function formatOperand(op) {
|
|
20
|
+
switch (op.kind) {
|
|
21
|
+
case 'reg':
|
|
22
|
+
return op.name.toLowerCase();
|
|
23
|
+
case 'imm':
|
|
24
|
+
return formatLoweredImmExpr(op.expr);
|
|
25
|
+
case 'ea':
|
|
26
|
+
return formatEaExpr(op.expr);
|
|
27
|
+
case 'mem':
|
|
28
|
+
return `(${formatEaExpr(op.expr)})`;
|
|
29
|
+
case 'portImm8':
|
|
30
|
+
return `(${formatLoweredImmExpr(op.expr)})`;
|
|
31
|
+
case 'portC':
|
|
32
|
+
return '(c)';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function formatItem(item) {
|
|
36
|
+
switch (item.kind) {
|
|
37
|
+
case 'label':
|
|
38
|
+
return [`${item.name}:`];
|
|
39
|
+
case 'const':
|
|
40
|
+
return [`${item.name} EQU ${formatLoweredImmExpr(item.value)}`];
|
|
41
|
+
case 'comment':
|
|
42
|
+
if (!item.text.trim())
|
|
43
|
+
return [];
|
|
44
|
+
if (item.origin === 'user')
|
|
45
|
+
return [`; ${item.text}`];
|
|
46
|
+
return [`; AZM: ${item.text}`];
|
|
47
|
+
case 'db':
|
|
48
|
+
return [`DB ${item.values.map(formatLoweredImmExpr).join(', ')}`];
|
|
49
|
+
case 'dw':
|
|
50
|
+
return [`DW ${item.values.map(formatLoweredImmExpr).join(', ')}`];
|
|
51
|
+
case 'ds':
|
|
52
|
+
return [
|
|
53
|
+
item.fill === undefined
|
|
54
|
+
? `DS ${formatLoweredImmExpr(item.size)}`
|
|
55
|
+
: `DS ${formatLoweredImmExpr(item.size)}, ${formatLoweredImmExpr(item.fill)}`,
|
|
56
|
+
];
|
|
57
|
+
case 'instr': {
|
|
58
|
+
if (item.head === '@raw') {
|
|
59
|
+
const bytes = item.bytes ?? [];
|
|
60
|
+
if (bytes.length === 0)
|
|
61
|
+
return [];
|
|
62
|
+
const parts = bytes.map((b) => `$${toHexByte(b & 0xff)}`);
|
|
63
|
+
return [`DB ${parts.join(', ')}`];
|
|
64
|
+
}
|
|
65
|
+
const head = item.head.toLowerCase();
|
|
66
|
+
const ops = item.operands.map(formatOperand);
|
|
67
|
+
return [ops.length ? `${head} ${ops.join(', ')}` : head];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Emit ASM80-compatible source from a placed lowered assembly program.
|
|
73
|
+
*/
|
|
74
|
+
export function writeAsm80(program, opts) {
|
|
75
|
+
const lineEnding = opts?.lineEnding ?? '\n';
|
|
76
|
+
const lines = [];
|
|
77
|
+
lines.push('; AZM lowered ASM80 output');
|
|
78
|
+
for (const block of program.blocks) {
|
|
79
|
+
lines.push('');
|
|
80
|
+
lines.push(`ORG ${formatLoweredNumber(block.origin)}`);
|
|
81
|
+
for (const item of block.items) {
|
|
82
|
+
lines.push(...formatItem(item));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { kind: 'asm80', text: lines.join(lineEnding) + lineEnding };
|
|
86
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BinArtifact, EmittedByteMap, SymbolEntry, WriteBinOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a flat binary artifact from an emitted address->byte map.
|
|
4
|
+
*
|
|
5
|
+
* Bytes are emitted for the computed written range; unwritten addresses inside the range are `0x00`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function writeBin(map: EmittedByteMap, symbols: SymbolEntry[], opts?: WriteBinOptions): BinArtifact;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getWrittenRange } from './range.js';
|
|
2
|
+
const BINFROM_SYMBOL_NAME = '__azm_binfrom';
|
|
3
|
+
const BINTO_SYMBOL_NAME = '__azm_binto';
|
|
4
|
+
/**
|
|
5
|
+
* Create a flat binary artifact from an emitted address->byte map.
|
|
6
|
+
*
|
|
7
|
+
* Bytes are emitted for the computed written range; unwritten addresses inside the range are `0x00`.
|
|
8
|
+
*/
|
|
9
|
+
export function writeBin(map, symbols, opts) {
|
|
10
|
+
const { start: writtenStart, end } = getWrittenRange(map);
|
|
11
|
+
const optionStart = opts?.binFrom ??
|
|
12
|
+
opts?.startAddress;
|
|
13
|
+
const symbolStart = symbols.find((symbol) => symbol.kind === 'constant' && symbol.name === BINFROM_SYMBOL_NAME);
|
|
14
|
+
const symbolEnd = symbols.find((symbol) => symbol.kind === 'constant' && symbol.name === BINTO_SYMBOL_NAME);
|
|
15
|
+
const start = optionStart ?? (symbolStart?.kind === 'constant' ? symbolStart.value : writtenStart);
|
|
16
|
+
const exclusiveEnd = symbolEnd?.kind === 'constant' ? symbolEnd.value + 1 : end;
|
|
17
|
+
const out = new Uint8Array(Math.max(0, exclusiveEnd - start));
|
|
18
|
+
for (let i = 0; i < out.length; i++) {
|
|
19
|
+
const addr = start + i;
|
|
20
|
+
out[i] = map.bytes.get(addr) ?? 0;
|
|
21
|
+
}
|
|
22
|
+
return { kind: 'bin', bytes: out };
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { D8mArtifact, EmittedByteMap, SymbolEntry, WriteD8mOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a minimal D8 Debug Map (D8M) v1 JSON artifact.
|
|
4
|
+
*
|
|
5
|
+
* Implementation note:
|
|
6
|
+
* - Emits baseline contiguous written segments plus optional source-attributed per-file segments
|
|
7
|
+
* when provided by lowering.
|
|
8
|
+
*/
|
|
9
|
+
export declare function writeD8m(map: EmittedByteMap, symbols: SymbolEntry[], opts?: WriteD8mOptions): D8mArtifact;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
2
|
+
import { getWrittenRange, getWrittenSegments } from './range.js';
|
|
3
|
+
function normalizeD8mPath(file, rootDir) {
|
|
4
|
+
const withSlashes = file.replace(/\\/g, '/');
|
|
5
|
+
if (!rootDir)
|
|
6
|
+
return withSlashes;
|
|
7
|
+
const absFile = resolve(file);
|
|
8
|
+
const absRoot = resolve(rootDir);
|
|
9
|
+
const rel = relative(absRoot, absFile);
|
|
10
|
+
if (!rel || rel.startsWith('..') || isAbsolute(rel)) {
|
|
11
|
+
return absFile.replace(/\\/g, '/');
|
|
12
|
+
}
|
|
13
|
+
return rel.replace(/\\/g, '/');
|
|
14
|
+
}
|
|
15
|
+
function toSerializedSymbol(symbol) {
|
|
16
|
+
if (symbol.kind === 'constant') {
|
|
17
|
+
return {
|
|
18
|
+
name: symbol.name,
|
|
19
|
+
kind: 'constant',
|
|
20
|
+
value: symbol.value,
|
|
21
|
+
...(symbol.file !== undefined ? { file: symbol.file } : {}),
|
|
22
|
+
...(symbol.line !== undefined ? { line: symbol.line } : {}),
|
|
23
|
+
...(symbol.scope !== undefined ? { scope: symbol.scope } : {}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
name: symbol.name,
|
|
28
|
+
kind: symbol.kind,
|
|
29
|
+
address: symbol.address,
|
|
30
|
+
...(symbol.file !== undefined ? { file: symbol.file } : {}),
|
|
31
|
+
...(symbol.line !== undefined ? { line: symbol.line } : {}),
|
|
32
|
+
...(symbol.scope !== undefined ? { scope: symbol.scope } : {}),
|
|
33
|
+
...(symbol.size !== undefined ? { size: symbol.size } : {}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function compareSerializedSymbols(a, b) {
|
|
37
|
+
const aClass = a.kind === 'constant' ? 1 : 0;
|
|
38
|
+
const bClass = b.kind === 'constant' ? 1 : 0;
|
|
39
|
+
if (aClass !== bClass)
|
|
40
|
+
return aClass - bClass;
|
|
41
|
+
const aAddress = a.kind === 'constant' ? a.value & 0xffff : a.address;
|
|
42
|
+
const bAddress = b.kind === 'constant' ? b.value & 0xffff : b.address;
|
|
43
|
+
if (aAddress !== bAddress)
|
|
44
|
+
return aAddress - bAddress;
|
|
45
|
+
const nameCmp = a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
|
46
|
+
if (nameCmp !== 0)
|
|
47
|
+
return nameCmp;
|
|
48
|
+
const kindCmp = a.kind.localeCompare(b.kind);
|
|
49
|
+
if (kindCmp !== 0)
|
|
50
|
+
return kindCmp;
|
|
51
|
+
const fileCmp = (a.file ?? '').localeCompare(b.file ?? '');
|
|
52
|
+
if (fileCmp !== 0)
|
|
53
|
+
return fileCmp;
|
|
54
|
+
const lineCmp = (a.line ?? 0) - (b.line ?? 0);
|
|
55
|
+
if (lineCmp !== 0)
|
|
56
|
+
return lineCmp;
|
|
57
|
+
if (a.kind === 'constant' && b.kind === 'constant') {
|
|
58
|
+
return a.value - b.value;
|
|
59
|
+
}
|
|
60
|
+
const aSize = a.size ?? 0;
|
|
61
|
+
const bSize = b.size ?? 0;
|
|
62
|
+
return aSize - bSize;
|
|
63
|
+
}
|
|
64
|
+
function compareFileSymbols(a, b) {
|
|
65
|
+
const withFile = (symbol) => ({
|
|
66
|
+
...symbol,
|
|
67
|
+
});
|
|
68
|
+
return compareSerializedSymbols(withFile(a), withFile(b));
|
|
69
|
+
}
|
|
70
|
+
function compareD8mSegments(a, b) {
|
|
71
|
+
if (a.start !== b.start)
|
|
72
|
+
return a.start - b.start;
|
|
73
|
+
if (a.end !== b.end)
|
|
74
|
+
return a.end - b.end;
|
|
75
|
+
if (a.lstLine !== b.lstLine)
|
|
76
|
+
return a.lstLine - b.lstLine;
|
|
77
|
+
const aLine = a.line ?? 0;
|
|
78
|
+
const bLine = b.line ?? 0;
|
|
79
|
+
if (aLine !== bLine)
|
|
80
|
+
return aLine - bLine;
|
|
81
|
+
const kindCmp = a.kind.localeCompare(b.kind);
|
|
82
|
+
if (kindCmp !== 0)
|
|
83
|
+
return kindCmp;
|
|
84
|
+
return a.confidence.localeCompare(b.confidence);
|
|
85
|
+
}
|
|
86
|
+
function rangesOverlap(a, b) {
|
|
87
|
+
return a.start < b.end && b.start < a.end;
|
|
88
|
+
}
|
|
89
|
+
function hasOverlappingSourceSegment(sourceSegmentsByFile, file, range) {
|
|
90
|
+
return (sourceSegmentsByFile.get(file) ?? []).some((segment) => rangesOverlap({ start: segment.start, end: segment.end }, range));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Create a minimal D8 Debug Map (D8M) v1 JSON artifact.
|
|
94
|
+
*
|
|
95
|
+
* Implementation note:
|
|
96
|
+
* - Emits baseline contiguous written segments plus optional source-attributed per-file segments
|
|
97
|
+
* when provided by lowering.
|
|
98
|
+
*/
|
|
99
|
+
export function writeD8m(map, symbols, opts) {
|
|
100
|
+
const { start, end } = getWrittenRange(map);
|
|
101
|
+
const writtenSegments = getWrittenSegments(map);
|
|
102
|
+
const segments = writtenSegments.length > 0
|
|
103
|
+
? writtenSegments
|
|
104
|
+
: start < end
|
|
105
|
+
? [{ start, end }]
|
|
106
|
+
: [{ start: 0, end: 0 }];
|
|
107
|
+
const normalizedSymbols = symbols.map((s) => ({
|
|
108
|
+
...s,
|
|
109
|
+
...(s.file !== undefined ? { file: normalizeD8mPath(s.file, opts?.rootDir) } : {}),
|
|
110
|
+
}));
|
|
111
|
+
const normalizedSourceSegments = (map.sourceSegments ?? [])
|
|
112
|
+
.filter((segment) => segment.end > segment.start)
|
|
113
|
+
.map((segment) => ({
|
|
114
|
+
...segment,
|
|
115
|
+
file: normalizeD8mPath(segment.file, opts?.rootDir),
|
|
116
|
+
}));
|
|
117
|
+
const fileSet = new Set(normalizedSymbols
|
|
118
|
+
.map((s) => s.file)
|
|
119
|
+
.filter((f) => typeof f === 'string' && f.length > 0));
|
|
120
|
+
for (const segment of normalizedSourceSegments) {
|
|
121
|
+
if (segment.file.length > 0)
|
|
122
|
+
fileSet.add(segment.file);
|
|
123
|
+
}
|
|
124
|
+
const fileList = Array.from(fileSet).sort((a, b) => a.localeCompare(b));
|
|
125
|
+
const serializedSymbols = normalizedSymbols
|
|
126
|
+
.map(toSerializedSymbol)
|
|
127
|
+
.sort(compareSerializedSymbols);
|
|
128
|
+
const fileEntries = new Map();
|
|
129
|
+
const ensureFileEntry = (path) => {
|
|
130
|
+
let entry = fileEntries.get(path);
|
|
131
|
+
if (!entry) {
|
|
132
|
+
entry = { symbols: [], segments: [] };
|
|
133
|
+
fileEntries.set(path, entry);
|
|
134
|
+
}
|
|
135
|
+
return entry;
|
|
136
|
+
};
|
|
137
|
+
for (const symbol of serializedSymbols) {
|
|
138
|
+
const key = symbol.file ?? '';
|
|
139
|
+
const entry = ensureFileEntry(key);
|
|
140
|
+
const { file: _file, ...withoutFile } = symbol;
|
|
141
|
+
entry.symbols.push(withoutFile);
|
|
142
|
+
}
|
|
143
|
+
for (const segment of normalizedSourceSegments) {
|
|
144
|
+
ensureFileEntry(segment.file).segments.push({
|
|
145
|
+
start: segment.start,
|
|
146
|
+
end: segment.end,
|
|
147
|
+
lstLine: segment.line,
|
|
148
|
+
line: segment.line,
|
|
149
|
+
kind: segment.kind,
|
|
150
|
+
confidence: segment.confidence,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
const sourceSegmentsByFile = new Map();
|
|
154
|
+
for (const segment of normalizedSourceSegments) {
|
|
155
|
+
const entries = sourceSegmentsByFile.get(segment.file);
|
|
156
|
+
if (entries) {
|
|
157
|
+
entries.push(segment);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
sourceSegmentsByFile.set(segment.file, [segment]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const symbolRangesByFile = new Map();
|
|
164
|
+
for (const symbol of serializedSymbols) {
|
|
165
|
+
if (symbol.kind === 'constant' || symbol.file === undefined)
|
|
166
|
+
continue;
|
|
167
|
+
const spanSize = symbol.size !== undefined && symbol.size > 0 ? symbol.size : 1;
|
|
168
|
+
const range = {
|
|
169
|
+
start: symbol.address,
|
|
170
|
+
end: Math.min(0x10000, symbol.address + spanSize),
|
|
171
|
+
};
|
|
172
|
+
const currentRanges = symbolRangesByFile.get(symbol.file);
|
|
173
|
+
if (currentRanges) {
|
|
174
|
+
currentRanges.push(range);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
symbolRangesByFile.set(symbol.file, [range]);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (const ranges of symbolRangesByFile.values()) {
|
|
181
|
+
ranges.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
182
|
+
}
|
|
183
|
+
for (const segment of segments) {
|
|
184
|
+
const segmentRange = { start: segment.start, end: segment.end };
|
|
185
|
+
const fileKeys = Array.from(symbolRangesByFile.entries())
|
|
186
|
+
.filter(([, ranges]) => ranges.some((range) => rangesOverlap(range, segmentRange)))
|
|
187
|
+
.map(([path]) => path)
|
|
188
|
+
.sort((a, b) => a.localeCompare(b));
|
|
189
|
+
const targets = fileKeys.length > 0 ? fileKeys : [fileList[0] ?? ''];
|
|
190
|
+
for (const target of targets) {
|
|
191
|
+
if (hasOverlappingSourceSegment(sourceSegmentsByFile, target, segmentRange))
|
|
192
|
+
continue;
|
|
193
|
+
ensureFileEntry(target).segments.push({
|
|
194
|
+
start: segment.start,
|
|
195
|
+
end: segment.end,
|
|
196
|
+
// Synthetic file-attribution segments have no exact source line; use a valid fallback.
|
|
197
|
+
lstLine: 1,
|
|
198
|
+
kind: 'unknown',
|
|
199
|
+
confidence: 'low',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
for (const entry of fileEntries.values()) {
|
|
204
|
+
entry.symbols.sort(compareFileSymbols);
|
|
205
|
+
entry.segments.sort(compareD8mSegments);
|
|
206
|
+
}
|
|
207
|
+
const files = Object.fromEntries(Array.from(fileEntries.entries())
|
|
208
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
209
|
+
.map(([path, entry]) => [
|
|
210
|
+
path,
|
|
211
|
+
{
|
|
212
|
+
...(entry.segments.length > 0 ? { segments: entry.segments } : {}),
|
|
213
|
+
...(entry.symbols.length > 0 ? { symbols: entry.symbols } : {}),
|
|
214
|
+
},
|
|
215
|
+
]));
|
|
216
|
+
const json = {
|
|
217
|
+
format: 'd8-debug-map',
|
|
218
|
+
version: 1,
|
|
219
|
+
arch: 'z80',
|
|
220
|
+
addressWidth: 16,
|
|
221
|
+
endianness: 'little',
|
|
222
|
+
files,
|
|
223
|
+
segments,
|
|
224
|
+
...(fileList.length > 0 ? { fileList } : {}),
|
|
225
|
+
symbols: serializedSymbols,
|
|
226
|
+
...(opts?.entrySymbol !== undefined || opts?.entryAddress !== undefined
|
|
227
|
+
? {
|
|
228
|
+
generator: {
|
|
229
|
+
tool: 'azm',
|
|
230
|
+
...(opts.entrySymbol !== undefined ? { entrySymbol: opts.entrySymbol } : {}),
|
|
231
|
+
...(opts.entryAddress !== undefined
|
|
232
|
+
? { entryAddress: opts.entryAddress & 0xffff }
|
|
233
|
+
: {}),
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
: {}),
|
|
237
|
+
};
|
|
238
|
+
return { kind: 'd8m', json };
|
|
239
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EmittedByteMap, HexArtifact, SymbolEntry, WriteHexOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create an Intel HEX artifact from an emitted address->byte map.
|
|
4
|
+
*
|
|
5
|
+
* PR1 implementation note:
|
|
6
|
+
* - Emits only type-00 data records and a type-01 EOF record.
|
|
7
|
+
* - Does not emit extended address records (assumes 16-bit address space).
|
|
8
|
+
*/
|
|
9
|
+
export declare function writeHex(map: EmittedByteMap, _symbols: SymbolEntry[], opts?: WriteHexOptions): HexArtifact;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { getWrittenSegments } from './range.js';
|
|
2
|
+
function toHexByte(n) {
|
|
3
|
+
return (n & 0xff).toString(16).toUpperCase().padStart(2, '0');
|
|
4
|
+
}
|
|
5
|
+
function checksum(bytes) {
|
|
6
|
+
const sum = bytes.reduce((acc, b) => acc + (b & 0xff), 0) & 0xff;
|
|
7
|
+
return ((0x100 - sum) & 0xff) >>> 0;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Create an Intel HEX artifact from an emitted address->byte map.
|
|
11
|
+
*
|
|
12
|
+
* PR1 implementation note:
|
|
13
|
+
* - Emits only type-00 data records and a type-01 EOF record.
|
|
14
|
+
* - Does not emit extended address records (assumes 16-bit address space).
|
|
15
|
+
*/
|
|
16
|
+
export function writeHex(map, _symbols, opts) {
|
|
17
|
+
const lineEnding = opts?.lineEnding ?? '\n';
|
|
18
|
+
const segments = getWrittenSegments(map);
|
|
19
|
+
const recordSize = 16;
|
|
20
|
+
const lines = [];
|
|
21
|
+
for (const segment of segments) {
|
|
22
|
+
for (let addr = segment.start; addr < segment.end; addr += recordSize) {
|
|
23
|
+
const count = Math.min(recordSize, segment.end - addr);
|
|
24
|
+
const data = [];
|
|
25
|
+
for (let i = 0; i < count; i++) {
|
|
26
|
+
data.push(map.bytes.get(addr + i) ?? 0);
|
|
27
|
+
}
|
|
28
|
+
const hi = (addr >> 8) & 0xff;
|
|
29
|
+
const lo = addr & 0xff;
|
|
30
|
+
const recType = 0x00;
|
|
31
|
+
const header = [count, hi, lo, recType, ...data];
|
|
32
|
+
const cs = checksum(header);
|
|
33
|
+
const hexData = data.map(toHexByte).join('');
|
|
34
|
+
lines.push(`:${toHexByte(count)}${toHexByte(hi)}${toHexByte(lo)}00${hexData}${toHexByte(cs)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
lines.push(':00000001FF');
|
|
38
|
+
return { kind: 'hex', text: lines.join(lineEnding) + lineEnding };
|
|
39
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { EmittedByteMap, ListingArtifact, SymbolEntry, WriteListingOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a deterministic `.lst` listing artifact.
|
|
4
|
+
*
|
|
5
|
+
* Current implementation is a stable byte dump + symbol table. It does not yet include per-instruction
|
|
6
|
+
* source mapping; D8M should be used for debuggers.
|
|
7
|
+
*/
|
|
8
|
+
export declare function writeListing(map: EmittedByteMap, symbols: SymbolEntry[], opts?: WriteListingOptions): ListingArtifact;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { getWrittenRange, getWrittenSegments } from './range.js';
|
|
2
|
+
function toHexByte(n) {
|
|
3
|
+
return (n & 0xff).toString(16).toUpperCase().padStart(2, '0');
|
|
4
|
+
}
|
|
5
|
+
function toHexWord(n) {
|
|
6
|
+
return (n & 0xffff).toString(16).toUpperCase().padStart(4, '0');
|
|
7
|
+
}
|
|
8
|
+
function toAsciiByte(n) {
|
|
9
|
+
const v = n & 0xff;
|
|
10
|
+
return v >= 0x20 && v <= 0x7e ? String.fromCharCode(v) : '.';
|
|
11
|
+
}
|
|
12
|
+
function formatSymbol(s) {
|
|
13
|
+
if (s.kind === 'constant') {
|
|
14
|
+
const value = s.value & 0xffff;
|
|
15
|
+
return `${s.kind} ${s.name} = $${toHexWord(value)} (${s.value})`;
|
|
16
|
+
}
|
|
17
|
+
return `${s.kind} ${s.name} = $${toHexWord(s.address)}`;
|
|
18
|
+
}
|
|
19
|
+
function sortSymbols(a, b) {
|
|
20
|
+
const aKey = a.kind === 'constant'
|
|
21
|
+
? `1\n${toHexWord(a.value & 0xffff)}\n${a.name.toLowerCase()}`
|
|
22
|
+
: `0\n${toHexWord(a.address)}\n${a.name.toLowerCase()}`;
|
|
23
|
+
const bKey = b.kind === 'constant'
|
|
24
|
+
? `1\n${toHexWord(b.value & 0xffff)}\n${b.name.toLowerCase()}`
|
|
25
|
+
: `0\n${toHexWord(b.address)}\n${b.name.toLowerCase()}`;
|
|
26
|
+
return aKey.localeCompare(bKey);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Create a deterministic `.lst` listing artifact.
|
|
30
|
+
*
|
|
31
|
+
* Current implementation is a stable byte dump + symbol table. It does not yet include per-instruction
|
|
32
|
+
* source mapping; D8M should be used for debuggers.
|
|
33
|
+
*/
|
|
34
|
+
export function writeListing(map, symbols, opts) {
|
|
35
|
+
const lineEnding = opts?.lineEnding ?? '\n';
|
|
36
|
+
const bytesPerLine = opts?.bytesPerLine ?? 16;
|
|
37
|
+
const { start, end } = getWrittenRange(map);
|
|
38
|
+
const segments = getWrittenSegments(map);
|
|
39
|
+
const lines = [];
|
|
40
|
+
lines.push('; AZM listing');
|
|
41
|
+
lines.push(`; range: $${toHexWord(start)}..$${toHexWord(end)} (end exclusive)`);
|
|
42
|
+
lines.push('');
|
|
43
|
+
const lineBaseSet = new Set();
|
|
44
|
+
for (const segment of segments) {
|
|
45
|
+
const first = segment.start - (segment.start % bytesPerLine);
|
|
46
|
+
const last = segment.end - 1 - ((segment.end - 1) % bytesPerLine);
|
|
47
|
+
for (let addr = first; addr <= last; addr += bytesPerLine) {
|
|
48
|
+
lineBaseSet.add(addr);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const lineBases = [...lineBaseSet].sort((a, b) => a - b);
|
|
52
|
+
let previousBase;
|
|
53
|
+
for (const addr of lineBases) {
|
|
54
|
+
if (previousBase !== undefined && addr > previousBase + bytesPerLine) {
|
|
55
|
+
const gapStart = previousBase + bytesPerLine;
|
|
56
|
+
const gapEndInclusive = addr - 1;
|
|
57
|
+
const gapLineCount = Math.ceil((addr - gapStart) / bytesPerLine);
|
|
58
|
+
lines.push(`; ... gap $${toHexWord(gapStart)}..$${toHexWord(gapEndInclusive)} (${gapLineCount} lines)`);
|
|
59
|
+
}
|
|
60
|
+
const count = Math.min(bytesPerLine, end - addr);
|
|
61
|
+
const hexBytes = [];
|
|
62
|
+
const asciiBytes = [];
|
|
63
|
+
for (let i = 0; i < count; i++) {
|
|
64
|
+
const byte = map.bytes.get(addr + i);
|
|
65
|
+
if (byte === undefined) {
|
|
66
|
+
hexBytes.push('..');
|
|
67
|
+
asciiBytes.push(' ');
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
hexBytes.push(toHexByte(byte));
|
|
71
|
+
asciiBytes.push(toAsciiByte(byte));
|
|
72
|
+
}
|
|
73
|
+
const paddedHex = hexBytes.join(' ').padEnd(bytesPerLine * 3 - 1, ' ');
|
|
74
|
+
lines.push(`${toHexWord(addr)}: ${paddedHex} |${asciiBytes.join('')}|`);
|
|
75
|
+
previousBase = addr;
|
|
76
|
+
}
|
|
77
|
+
lines.push('');
|
|
78
|
+
lines.push('; symbols:');
|
|
79
|
+
for (const s of [...symbols].sort(sortSymbols)) {
|
|
80
|
+
lines.push(`; ${formatSymbol(s)}`);
|
|
81
|
+
}
|
|
82
|
+
return { kind: 'lst', text: lines.join(lineEnding) + lineEnding };
|
|
83
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { DirectiveAliasPolicy } from '../directiveAliases.js';
|
|
2
|
+
type AsmLine = {
|
|
3
|
+
kind: 'label';
|
|
4
|
+
name: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'equ';
|
|
7
|
+
name: string;
|
|
8
|
+
exprText: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'org';
|
|
11
|
+
exprText: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'align';
|
|
14
|
+
exprText: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'binfrom';
|
|
17
|
+
exprText: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'binto';
|
|
20
|
+
exprText: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'end';
|
|
23
|
+
} | {
|
|
24
|
+
kind: 'unsupportedDirective';
|
|
25
|
+
label?: string;
|
|
26
|
+
directive: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'rawData';
|
|
29
|
+
label?: string;
|
|
30
|
+
directive: 'db' | 'dw' | 'ds' | 'cstr' | 'pstr' | 'istr';
|
|
31
|
+
valuesText: string;
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'instruction';
|
|
34
|
+
label?: string;
|
|
35
|
+
head: string;
|
|
36
|
+
operandText: string;
|
|
37
|
+
};
|
|
38
|
+
export declare function parseAsmLine(_filePath: string, text: string, _lineNo: number, _lineStartOffset: number, aliasPolicy?: DirectiveAliasPolicy): AsmLine | undefined;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { resolveDirectiveAlias } from '../directiveAliases.js';
|
|
2
|
+
import { advanceAsmQuoteScan, createAsmQuoteScanState } from './quoteScan.js';
|
|
3
|
+
export function parseAsmLine(_filePath, text, _lineNo, _lineStartOffset, aliasPolicy) {
|
|
4
|
+
const stripped = stripAsm80Comment(text).trim();
|
|
5
|
+
if (stripped.length === 0)
|
|
6
|
+
return undefined;
|
|
7
|
+
const colonLabel = /^(@?[A-Za-z_][A-Za-z0-9_]*|\.[A-Za-z_][A-Za-z0-9_]*):\s*(.*)$/.exec(stripped);
|
|
8
|
+
if (colonLabel) {
|
|
9
|
+
const label = colonLabel[1];
|
|
10
|
+
const rest = colonLabel[2].trim();
|
|
11
|
+
if (rest.length === 0)
|
|
12
|
+
return { kind: 'label', name: label };
|
|
13
|
+
return parseStatement(rest, aliasPolicy, label);
|
|
14
|
+
}
|
|
15
|
+
const equLabel = /^([A-Za-z_][A-Za-z0-9_]*)\s+([.]?[A-Za-z][A-Za-z0-9_]*)\b\s*(.+)$/i.exec(stripped);
|
|
16
|
+
if (equLabel && resolveDirectiveAlias(equLabel[2], aliasPolicy) === '.equ') {
|
|
17
|
+
return { kind: 'equ', name: equLabel[1], exprText: equLabel[3].trim() };
|
|
18
|
+
}
|
|
19
|
+
return parseStatement(stripped, aliasPolicy);
|
|
20
|
+
}
|
|
21
|
+
function stripAsm80Comment(text) {
|
|
22
|
+
const quoteState = createAsmQuoteScanState();
|
|
23
|
+
for (let i = 0; i < text.length; i++) {
|
|
24
|
+
if (advanceAsmQuoteScan(text, i, quoteState, {
|
|
25
|
+
singleQuoteStartsCharAt: (source, index) => index === 0 || !/[A-Za-z0-9_]/.test(source[index - 1]),
|
|
26
|
+
})) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!quoteState.inString && !quoteState.inChar && text[i] === ';')
|
|
30
|
+
return text.slice(0, i);
|
|
31
|
+
}
|
|
32
|
+
return text;
|
|
33
|
+
}
|
|
34
|
+
function parseStatement(text, aliasPolicy, label) {
|
|
35
|
+
const maybeDirective = /^([.]?[A-Za-z][A-Za-z0-9_]*)\b\s*(.*)$/.exec(text);
|
|
36
|
+
if (maybeDirective) {
|
|
37
|
+
const canonical = resolveDirectiveAlias(maybeDirective[1], aliasPolicy);
|
|
38
|
+
if (canonical === '.equ' && label) {
|
|
39
|
+
return { kind: 'equ', name: label, exprText: maybeDirective[2].trim() };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const directive = /^([.]?[A-Za-z][A-Za-z0-9_]*)\b\s*(.*)$/.exec(text);
|
|
43
|
+
if (directive) {
|
|
44
|
+
const canonical = resolveDirectiveAlias(directive[1], aliasPolicy);
|
|
45
|
+
const name = canonical?.slice(1);
|
|
46
|
+
const payload = directive[2].trim();
|
|
47
|
+
if (name === 'org')
|
|
48
|
+
return { kind: 'org', exprText: payload };
|
|
49
|
+
if (name === 'align')
|
|
50
|
+
return { kind: 'align', exprText: payload };
|
|
51
|
+
if (name === 'binfrom')
|
|
52
|
+
return { kind: 'binfrom', exprText: payload };
|
|
53
|
+
if (name === 'binto')
|
|
54
|
+
return { kind: 'binto', exprText: payload };
|
|
55
|
+
if (name === 'end')
|
|
56
|
+
return { kind: 'end' };
|
|
57
|
+
if (name === 'db' ||
|
|
58
|
+
name === 'dw' ||
|
|
59
|
+
name === 'ds' ||
|
|
60
|
+
name === 'cstr' ||
|
|
61
|
+
name === 'pstr' ||
|
|
62
|
+
name === 'istr') {
|
|
63
|
+
return { kind: 'rawData', ...(label ? { label } : {}), directive: name, valuesText: payload };
|
|
64
|
+
}
|
|
65
|
+
if (text.trimStart().startsWith('.')) {
|
|
66
|
+
return {
|
|
67
|
+
kind: 'unsupportedDirective',
|
|
68
|
+
...(label ? { label } : {}),
|
|
69
|
+
directive: directive[1].replace(/^\./, '').toLowerCase(),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (name === 'macro' ||
|
|
73
|
+
name === 'rept' ||
|
|
74
|
+
name === 'endm' ||
|
|
75
|
+
name === 'block' ||
|
|
76
|
+
name === 'endblock') {
|
|
77
|
+
return { kind: 'unsupportedDirective', ...(label ? { label } : {}), directive: name };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const instruction = /^([A-Za-z][A-Za-z0-9_]*)(?:\s+(.*))?$/.exec(text);
|
|
81
|
+
if (!instruction)
|
|
82
|
+
return undefined;
|
|
83
|
+
return {
|
|
84
|
+
kind: 'instruction',
|
|
85
|
+
...(label ? { label } : {}),
|
|
86
|
+
head: instruction[1].toLowerCase(),
|
|
87
|
+
operandText: instruction[2]?.trim() ?? '',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Diagnostic } from '../../diagnosticTypes.js';
|
|
2
|
+
import type { SourceSpan } from '../ast.js';
|
|
3
|
+
export declare function parseAsmRawValues(path: string, valuesText: string, lineSpan: SourceSpan, diagnostics: Diagnostic[], stringEquates: Map<string, string>): unknown[];
|
|
4
|
+
export declare function parseWholeQuotedString(text: string): string | undefined;
|