2 // RMAC - Renamed Macro Assembler for all Atari computers
3 // SECT.C - Code Generation, Fixups and Section Management
4 // Copyright (C) 199x Landon Dyer, 2011-2021 Reboot and Friends
5 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
6 // Source utilised with the kind permission of Landon Dyer
24 // Function prototypes
25 void MakeSection(int, uint16_t);
26 void SwitchSection(int);
28 // Section descriptors
29 SECT sect[NSECTS]; // All sections...
30 int cursect; // Current section number
32 // These are copied from the section descriptor, the current code chunk
33 // descriptor and the current fixup chunk descriptor when a switch is made into
34 // a section. They are copied back to the descriptors when the section is left.
35 uint16_t scattr; // Section attributes
36 uint32_t sloc; // Current loc in section
38 CHUNK * scode; // Current (last) code chunk
39 uint32_t challoc; // # bytes alloc'd to code chunk
40 uint32_t ch_size; // # bytes used in code chunk
41 uint8_t * chptr; // Deposit point in code chunk buffer
42 uint8_t * chptr_opcode; // Backup of chptr, updated before entering code generators
44 // Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is
45 // associated with a location.
46 static uint8_t fusiztab[] = {
57 // Offset to REAL fixup location
58 static uint8_t fusizoffs[] = {
71 // Initialize sections; setup initial ABS, TEXT, DATA and BSS sections
73 void InitSection(void)
75 // Initialize all sections
76 for(int i=0; i<NSECTS; i++)
79 // Construct default sections, make TEXT the current section
80 MakeSection(ABS, SUSED | SABS | SBSS); // ABS
81 MakeSection(TEXT, SUSED | TEXT ); // TEXT
82 MakeSection(DATA, SUSED | DATA ); // DATA
83 MakeSection(BSS, SUSED | BSS | SBSS); // BSS
84 MakeSection(M6502, SUSED | TEXT ); // 6502 code section
85 MakeSection(M56001P, SUSED | SABS ); // DSP 56001 Program RAM
86 MakeSection(M56001X, SUSED | SABS ); // DSP 56001 X RAM
87 MakeSection(M56001Y, SUSED | SABS ); // DSP 56001 Y RAM
89 // Switch to TEXT for starters
95 // Make a new (clean) section
97 void MakeSection(int sno, uint16_t attr)
99 SECT * sp = §[sno];
103 sp->scode = sp->sfcode = NULL;
104 sp->sfix = sp->sffix = NULL;
109 // Switch to another section (copy section & chunk descriptors to global vars
112 void SwitchSection(int sno)
116 SECT * sp = §[sno];
118 m6502 = (sno == M6502); // Set 6502-mode flag
124 orgaddr = sp->orgaddr;
126 // Copy code chunk vars
127 if ((cp = scode) != NULL)
129 challoc = cp->challoc;
130 ch_size = cp->ch_size;
131 chptr = cp->chptr + ch_size;
133 // For 6502 mode, add the last org'd address
136 Because the way this is set up it treats the 6502 assembly space as a single 64K space (+ 16 bytes, for some unknown reason) and just bobbles around inside that space and uses a stack of org "pointers" to show where the data ended up.
138 This is a shitty way to handle things, and we can do better than this! :-P
140 Really, there's no reason to have the 6502 (or DSP56001 for that matter) have their own private sections for this kind of thing, as there's literally *no* chance that it would be mingled with 68K+ code. It should be able to use the TEXT, DATA & BSS sections just like the 68K.
142 Or should it? After looking at the code, maybe it's better to keep the 56001 sections segregated from the rest. But we can still make the 6502 stuff better.
145 chptr = cp->chptr + orgaddr;
148 challoc = ch_size = 0;
153 // Save current section
155 void SaveSection(void)
157 SECT * sp = §[cursect];
159 sp->scattr = scattr; // Bailout section vars
161 sp->orgaddr = orgaddr;
163 if (scode != NULL) // Bailout code chunk (if any)
164 scode->ch_size = ch_size;
169 // Test to see if a location has a fixup set on it. This is used by the
170 // listing generator to print 'xx's instead of '00's for forward references
172 int fixtest(int sno, uint32_t loc)
174 // Force update to sect[] variables
177 // Ugly linear search for a mark on our location. The speed doesn't
178 // matter, since this is only done when generating a listing, which is
180 for(FIXUP * fp=sect[sno].sffix; fp!=NULL; fp=fp->next)
182 uint32_t w = fp->attr;
183 uint32_t xloc = fp->loc + (int)fusizoffs[w & FUMASK];
186 return (int)fusiztab[w & FUMASK];
194 // Check that there are at least 'amt' bytes left in the current chunk. If
195 // there are not, allocate another chunk of at least CH_CODE_SIZE bytes or
196 // 'amt', whichever is larger.
198 // If 'amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
200 void chcheck(uint32_t amt)
202 DEBUG { printf("chcheck(%u)\n", amt); }
204 // If in BSS section, no allocation required
211 DEBUG { printf(" challoc=%i, ch_size=%i, diff=%i\n", challoc, ch_size, challoc - ch_size); }
213 if ((int)(challoc - ch_size) >= (int)amt)
216 if (amt < CH_CODE_SIZE)
219 DEBUG { printf(" amt (adjusted)=%u\n", amt); }
220 SECT * p = §[cursect];
221 CHUNK * cp = malloc(sizeof(CHUNK) + amt);
226 // First chunk in section
233 // Add second and on to previous chunk
236 scode->ch_size = ch_size; // Save old chunk's globals
239 // Setup chunk and global vars
241 So, whenever there's an ORG in a 56K section, it sets sloc TO THE ADDRESS IN THE ORG. Also, the loc/sloc are incremented by 1s, which means to alias correctly to the byte-oriented memory model we have here, we have to fix that kind of crap.
243 cp->chloc = sloc; // <-- HERE'S THE PROBLEM FOR 56K :-/
245 challoc = cp->challoc = amt;
246 ch_size = cp->ch_size = 0;
247 chptr = cp->chptr = ((uint8_t *)cp) + sizeof(CHUNK);
248 scode = p->scode = cp;
252 OK, so this is a bit shite, but at least it gets things working the way they should. The right way to do this is not rely on sloc & friends for the right fixup address but to have an accurate model of the thing. That will probably come with v2.0.1 :-P
254 So the problem is, d_org sets sloc to the address of the ORG statement, and that gives an incorrect base for the fixup. And so when a second (or later) chunk is allocated, it gets set wrong. Further complicating things is that the orgaddress *does not* get used in a typical way with the DSP56001 code, and, as such, causes incorrect addresses to be generated. All that has to be dealt with in order to get this right and do away with this kludge.
256 if (((cursect == M56001P) || (cursect == M56001X) || (cursect == M56001Y)) && !first)
257 cp->chloc = cp->chprev->chloc + cp->chprev->ch_size;
264 // Arrange for a fixup on a location
266 int AddFixup(uint32_t attr, uint32_t loc, TOKEN * fexpr)
268 uint16_t exprlen = 0;
270 uint32_t _orgaddr = 0;
272 // First, check to see if the expression is a bare label, otherwise, force
273 // the FU_EXPR flag into the attributes and count the tokens.
274 if ((fexpr[0] == SYMBOL) && (fexpr[2] == ENDEXPR))
276 symbol = symbolPtr[fexpr[1]];
278 // Save the org address for JR RISC instruction
279 if ((attr & FUMASKRISC) == FU_JR)
285 exprlen = ExpressionLength(fexpr);
288 // Second, check to see if it's a DSP56001 fixup, and force the FU_56001
289 // flag into the attributes if so; also save the current org address.
290 if (attr & FUMASKDSP)
293 // Save the exact spot in this chunk where the fixup should go
294 _orgaddr = chptr - scode->chptr + scode->chloc;
297 // Allocate space for the fixup + any expression
298 FIXUP * fixup = malloc(sizeof(FIXUP) + (sizeof(TOKEN) * exprlen)*2);
300 // Store the relevant fixup information in the FIXUP
304 fixup->fileno = cfileno;
305 fixup->lineno = curlineno;
307 fixup->symbol = symbol;
308 fixup->orgaddr = _orgaddr;
310 // Copy the passed in expression to the FIXUP, if any
313 // Here we used to to a plain memcpy and punt on trying to evaluate the expression by then.
314 // As discussed in bug #176, this can lead to robustness issues because some symbols might
315 // have changed by the time we perform the relocations (think of a symbol that's SET multiple
316 // times). So instead we perform a symbol-by-symbol copy and check to see if there are any
317 // resolved symbols that can be evaluated immediately. Those, we replace with constants.
318 // Also of note: because "fixup" can be larger than what ExpressionLength() returns
319 // (due to constants taking up more space than symbols), we allocate twice as RAM as we should
320 // without expansions just to be on the safe side. The "correct" thing to do would be to
321 // modify ExpressionLength() to cater for defined symbols and return the exact amount of items.
323 fixup->expr = (TOKEN *)((uint8_t *)fixup + sizeof(FIXUP));
326 dstexpr.u32 = fixup->expr;
328 for (i = 0; i < exprlen; i++)
330 if (*fexpr == SYMBOL)
332 sy = symbolPtr[fexpr[1]];
333 if (sy->sattr & DEFINED && !(sy->sattr & (TDB| M56KPXYL|M6502)))
335 // Only convert symbols that are defined and are absolute
336 *dstexpr.u32++ = CONST;
337 *dstexpr.u64++ = sy->svalue;
343 // Just copy the symbol
344 *dstexpr.u32++ = *fexpr++;
345 *dstexpr.u32++ = *fexpr++;
349 else if (*fexpr == CONST || *fexpr == FCONST)
352 *dstexpr.u32++ = *fexpr++;
353 *dstexpr.u32++ = *fexpr++;
354 *dstexpr.u32++ = *fexpr++;
358 *dstexpr.u32++ = *fexpr++;
362 // Finally, put the FIXUP in the current section's linked list
363 if (sect[cursect].sffix == NULL)
365 sect[cursect].sffix = fixup;
366 sect[cursect].sfix = fixup;
370 sect[cursect].sfix->next = fixup;
371 sect[cursect].sfix = fixup;
374 DEBUG { printf("AddFixup: sno=%u, l#=%u, attr=$%X, loc=$%X, expr=%p, sym=%p, org=$%X\n", cursect, fixup->lineno, fixup->attr, fixup->loc, (void *)fixup->expr, (void *)fixup->symbol, fixup->orgaddr);
376 printf(" name: %s, value: $%lX\n", symbol->sname, symbol->svalue);
384 // Resolve fixups in the passed in section
386 int ResolveFixups(int sno)
388 SECT * sc = §[sno];
390 // "Cache" first chunk
391 CHUNK * cch = sc->sfcode;
393 // Can't fixup a section with nothing in it
397 // Wire the 6502 segment's size to its allocated size (64K)
399 cch->ch_size = cch->challoc;
401 // Get first fixup for the passed in section
402 FIXUP * fixup = sect[sno].sffix;
404 while (fixup != NULL)
406 // We do it this way because we have continues everywhere... :-P
410 uint32_t dw = fup->attr; // Fixup long (type + modes + flags)
411 uint32_t loc = fup->loc; // Location to fixup
412 cfileno = fup->fileno;
413 curlineno = fup->lineno;
414 DEBUG { printf("ResolveFixups: sect#=%u, l#=%u, attr=$%X, loc=$%X, expr=%p, sym=%p, org=$%X\n", sno, fup->lineno, fup->attr, fup->loc, (void *)fup->expr, (void *)fup->symbol, fup->orgaddr); }
416 // This is based on global vars cfileno, curfname :-P
417 // This approach is kinda meh as well. I think we can do better
419 SetFilenameForErrorReporting();
421 if ((sno == M56001P) || (sno == M56001X) || (sno == M56001Y) || (sno == M56001L))
424 // Search for chunk containing location to fix up; compute a
425 // pointer to the location (in the chunk). Often we will find the
426 // Fixup is in the "cached" chunk, so the linear-search is seldom
428 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
430 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
432 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
438 // Fixup (loc) is out of range--this should never happen!
439 // Once we call this function, it winds down immediately; it
445 // Location to fix (in current chunk)
446 // We use the address of the chunk that loc is actually in, then
447 // subtract the chunk's starting location from loc to get the offset
448 // into the current chunk.
449 uint8_t * locp = cch->chptr + (loc - cch->chloc);
451 uint16_t eattr = 0; // Expression attrib
452 SYM * esym = NULL; // External symbol involved in expr
453 uint64_t eval; // Expression value
454 uint16_t flags; // Mark flags
456 // Compute expression/symbol value and attributes
458 // Complex expression
461 // evexpr presumably issues the errors/warnings here
462 if (evexpr(fup->expr, &eval, &eattr, &esym) != OK)
466 if (!(esym->sattr & DEFINED))
468 // If our expression still has an undefined symbol at this stage, it's bad news.
469 // The linker is never going to resolve the expression, so that's an error.
470 error("cannot export complex expression with unresloved symbol '%s'", esym->sname);
474 if ((CHECK_OPTS(OPT_PC_RELATIVE)) && (eattr & (DEFINED | REFERENCED | EQUATED)) == (DEFINED | REFERENCED))
476 error("relocation not allowed when o30 is enabled");
483 SYM * sy = fup->symbol;
486 if ((CHECK_OPTS(OPT_PC_RELATIVE)) && (eattr & (DEFINED | REFERENCED | EQUATED)) == (DEFINED | REFERENCED))
488 error("relocation not allowed when o30 is enabled");
497 // If the symbol is not defined, but global, set esym to sy
498 if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
501 DEBUG { printf(" name: %s, value: $%" PRIX64 "\n", sy->sname, sy->svalue); }
504 uint16_t tdb = eattr & TDB;
506 // If the expression/symbol is undefined and no external symbol is
507 // involved, then that's an error.
508 if (!(eattr & DEFINED) && (esym == NULL))
516 // If a PC-relative fixup is undefined, its value is *not* subtracted
517 // from the location (that will happen in the linker when the external
518 // reference is resolved).
520 // PC-relative fixups must be DEFINED and either in the same section
521 // (whereupon the subtraction takes place) or ABS (with no subtract).
522 if ((dw & FU_PCREL) || (dw & FU_PCRELX))
530 // In this instruction the PC is located a DWORD away
536 // Allow cross-section PCREL fixups in Alcyon mode
537 if (prg_flag || (obj_format == RAW))
542 // Shouldn't there be a break here, since otherwise, it will point to the DATA section?
545 eval += sect[TEXT].sloc;
548 eval += sect[TEXT].sloc + sect[DATA].sloc;
551 error("invalid section");
557 // In this instruction the PC is located a DWORD away
561 if ((int64_t)eval > 0x7fff || (int64_t)eval < -32768)
566 error("PC-relative expr across sections");
571 if (optim_warn_flag && (dw & FU_LBRA) && (eval + 0x80 < 0x100))
572 warn("unoptimized short branch");
575 // Be sure to clear any TDB flags, since we handled it just now
580 // Handle fixup classes
583 // FU_BBRA fixes up a one-byte branch offset.
585 if (!(eattr & DEFINED))
587 error("external short branch");
593 if (eval + 0x80 >= 0x100)
598 if (*locp) // optim_flags[OPT_NULL_BRA] is stored there, check the comment in mach.s under m_br
605 warn("o6: bra.s with zero offset converted to NOP");
611 error("illegal bra.s with zero offset");
616 *++locp = (uint8_t)eval;
619 // Fixup one-byte value at locp + 1.
624 // Fixup one-byte forward references
626 if (!(eattr & DEFINED))
628 error("external byte reference");
634 error("non-absolute byte reference");
638 if ((dw & FU_PCREL) && ((eval + 0x80) >= 0x100))
643 if ((eval + 0x100) >= 0x200)
646 else if (eval >= 0x100)
649 *locp = (uint8_t)eval;
652 // Fixup high/low byte off word for 6502
654 if (!(eattr & DEFINED))
656 error("external byte reference");
660 *locp = (uint8_t)(eval >> 8);
664 if (!(eattr & DEFINED))
666 error("external byte reference");
670 *locp = (uint8_t)eval;
673 // Fixup WORD forward references; the word could be unaligned in the
674 // section buffer, so we have to be careful. (? careful about what?)
676 if ((dw & FUMASKRISC) == FU_JR)
678 int reg = (signed)((eval - ((fup->orgaddr ? fup->orgaddr : loc) + 2)) / 2);
680 if ((reg < -16) || (reg > 15))
682 error("relative jump out of range");
686 *locp |= ((uint8_t)reg >> 3) & 0x03;
688 *locp |= ((uint8_t)reg & 0x07) << 5;
691 else if ((dw & FUMASKRISC) == FU_NUM15)
693 if (((int)eval < -16) || ((int)eval > 15))
695 error("constant out of range (-16 - +15)");
699 *locp |= ((uint8_t)eval >> 3) & 0x03;
701 *locp |= ((uint8_t)eval & 0x07) << 5;
704 else if ((dw & FUMASKRISC) == FU_NUM31)
708 error("constant out of range (0-31)");
712 *locp |= ((uint8_t)eval >> 3) & 0x03;
714 *locp |= ((uint8_t)eval & 0x07) << 5;
717 else if ((dw & FUMASKRISC) == FU_NUM32)
719 if ((eval < 1) || (eval > 32))
721 error("constant out of range (1-32)");
728 eval = (eval == 32) ? 0 : eval;
729 *locp |= ((uint8_t)eval >> 3) & 0x03;
731 *locp |= ((uint8_t)eval & 0x07) << 5;
734 else if ((dw & FUMASKRISC) == FU_REGONE)
739 error("register one value out of range");
743 *locp |= ((uint8_t)eval >> 3) & 0x03;
745 *locp |= ((uint8_t)eval & 0x07) << 5;
748 else if ((dw & FUMASKRISC) == FU_REGTWO)
753 error("register two value out of range");
758 *locp |= (uint8_t)eval & 0x1F;
762 if (!(eattr & DEFINED))
769 MarkRelocatable(sno, loc, 0, flags, esym);
774 MarkRelocatable(sno, loc, tdb, MWORD, NULL);
778 if (eval + 0x10000 >= 0x20000)
783 // Range-check BRA and DBRA
786 if (eval + 0x8000 >= 0x10000)
789 else if (eval >= 0x10000)
794 // 6502 words are little endian, so handle that here
796 SETLE16(locp, 0, eval)
798 SETBE16(locp, 0, eval)
802 // Fixup LONG forward references; the long could be unaligned in the
803 // section buffer, so be careful (again).
807 if ((dw & FUMASKRISC) == FU_MOVEI)
809 // Long constant in MOVEI # is word-swapped, so fix it here
810 eval = WORDSWAP32(eval);
814 // If the symbol is undefined, make sure to pass the symbol in
815 // to the MarkRelocatable() function.
816 if (!(eattr & DEFINED))
817 MarkRelocatable(sno, loc, 0, flags, esym);
819 MarkRelocatable(sno, loc, tdb, flags, NULL);
821 SETBE32(locp, 0, eval);
824 // Fixup QUAD forward references (mainly used by the OP assembler)
828 uint64_t quad = GETBE64(locp, 0);
829 uint64_t addr = eval;
831 //Hmm, not sure how this can be set, since it's only set if it's a DSP56001 fixup or a FU_JR... :-/
833 // addr = fup->orgaddr;
835 eval = (quad & 0xFFFFFC0000FFFFFFLL) | ((addr & 0x3FFFF8) << 21);
837 else if (dw & FU_OBJDATA)
839 // If it's in a TEXT or DATA section, be sure to mark for a
842 MarkRelocatable(sno, loc, tdb, MQUAD, NULL);
844 uint64_t quad = GETBE64(locp, 0);
845 uint64_t addr = eval;
847 //Hmm, not sure how this can be set, since it's only set if it's a DSP56001 fixup or a FU_JR... :-/
849 // addr = fup->orgaddr;
851 eval = (quad & 0x000007FFFFFFFFFFLL) | ((addr & 0xFFFFF8) << 40);
854 SETBE64(locp, 0, eval);
857 // Fixup a 3-bit "QUICK" reference in bits 9..1
858 // (range of 1..8) in a word. [Really bits 1..3 in a byte.]
860 if (!(eattr & DEFINED))
862 error("External quick reference");
866 if ((eval < 1) || (eval > 8))
869 *locp |= (eval & 7) << 1;
872 // Fix up 6502 funny branch
876 if (eval + 0x80 >= 0x100)
879 *locp = (uint8_t)eval;
882 // Fixup DSP56001 addresses
884 switch (dw & FUMASKDSP)
886 // DSPIMM5 actually is clamped from 0 to 23 for our purposes
887 // and does not use the full 5 bit range.
891 error("immediate value must be between 0 and 23");
898 // This is a 12-bit address encoded into the lower 12
899 // bits of a DSP word
903 error("address out of range ($0-$FFF)");
907 locp[1] |= eval >> 8;
908 locp[2] = eval & 0xFF;
911 // This is a full DSP word containing Effective Address Extension
914 if (eval >= 0x1000000)
916 error("value out of range ($0-$FFFFFF)");
920 locp[0] = (uint8_t)((eval >> 16) & 0xFF);
921 locp[1] = (uint8_t)((eval >> 8) & 0xFF);
922 locp[2] = (uint8_t)(eval & 0xFF);
925 // This is a 16bit absolute address into a 24bit field
929 error("address out of range ($0-$FFFF)");
933 locp[1] = (uint8_t)(eval >> 8);
934 locp[2] = (uint8_t)eval;
937 // This is 12-bit immediate short data
938 // The upper nibble goes into the last byte's low nibble
939 // while the remainder 8 bits go into the 2nd byte.
943 error("immediate out of range ($0-$FFF)");
947 locp[1] = (uint8_t)eval;
948 locp[2] |= (uint8_t)(eval >> 8);
951 // This is 8-bit immediate short data
952 // which goes into the middle byte of a DSP word.
956 error("immediate out of range ($0-$FF)");
960 locp[1] = (uint8_t)eval;
963 // This is a 6 bit absoulte short address. It occupies the low 6
964 // bits of the middle byte of a DSP word.
968 error("address must be between 0 and 63");
975 // This is a 6 bit absoulte short address. It occupies the low 6
976 // bits of the middle byte of a DSP word.
978 if (eval < 0xFFFFFFC0)
980 error("address must be between $FFC0 and $FFFF");
984 locp[1] |= eval & 0x3F;
987 // Shamus: I'm pretty sure these don't make any sense...
989 warn("FU_DSPIMMFL8 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
993 warn("FU_DSPIMMFL16 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
997 warn("FU_DSPIMMFL24 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
1000 // Bad fixup type--this should *never* happen!
1007 // Fixup a 4-byte float
1009 warn("FU_FLOATSING missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
1012 // Fixup a 8-byte float
1014 warn("FU_FLOATDOUB missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
1017 // Fixup a 12-byte float
1019 warn("FU_FLOATEXT missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
1023 // Bad fixup type--this should *never* happen!
1024 // Once we call this function, it winds down immediately; it
1031 error("expression out of range");
1039 // Resolve all fixups
1041 int ResolveAllFixups(void)
1043 // Make undefined symbols GLOBL
1045 ForceUndefinedSymbolsGlobal();
1047 DEBUG printf("Resolving TEXT sections...\n");
1048 ResolveFixups(TEXT);
1049 DEBUG printf("Resolving DATA sections...\n");
1050 ResolveFixups(DATA);
1051 DEBUG printf("Resolving 6502 sections...\n");
1052 ResolveFixups(M6502); // Fixup 6502 section (if any)
1053 DEBUG printf("Resolving DSP56001 P: sections...\n");
1054 ResolveFixups(M56001P); // Fixup 56001 P: section (if any)
1055 DEBUG printf("Resolving DSP56001 X: sections...\n");
1056 ResolveFixups(M56001X); // Fixup 56001 X: section (if any)
1057 DEBUG printf("Resolving DSP56001 Y: sections...\n");
1058 ResolveFixups(M56001Y); // Fixup 56001 Y: section (if any)