11from collections import OrderedDict
2- from typing import List , Optional , Type , Tuple
2+ from typing import List , Optional , Type , Tuple , Union
33
44from ...util import log , options , utils
55
@@ -21,9 +21,10 @@ def __init__(
2121 name : str ,
2222 vram_start : Optional [int ],
2323 args : list ,
24- yaml ,
24+ yaml : Union [dict , list ],
25+ bss_size : Optional [int ] = None ,
2526 ):
26- self .bss_size : int = yaml . get ( " bss_size" , 0 ) if isinstance ( yaml , dict ) else 0
27+ self .bss_size = bss_size if bss_size is not None else self . parse_bss_size ( yaml )
2728
2829 super ().__init__ (
2930 rom_start ,
@@ -33,6 +34,7 @@ def __init__(
3334 vram_start ,
3435 args = args ,
3536 yaml = yaml ,
37+ bss_size = self .bss_size ,
3638 )
3739
3840 self .reported_file_split = False
@@ -41,17 +43,6 @@ def __init__(
4143 if self .align is None :
4244 self .align = 0x10
4345
44- @property
45- def needs_symbols (self ) -> bool :
46- return True
47-
48- @property
49- def vram_end (self ) -> Optional [int ]:
50- if self .vram_start is not None and self .size is not None :
51- return self .vram_start + self .size + self .bss_size
52- else :
53- return None
54-
5546 # Generates a placeholder segment for the auto_link_sections option
5647 def _generate_segment_from_all (
5748 self ,
@@ -62,13 +53,15 @@ def _generate_segment_from_all(
6253 rom_start : Optional [int ] = None ,
6354 rom_end : Optional [int ] = None ,
6455 vram_start : Optional [int ] = None ,
56+ bss_size : Optional [int ] = None ,
6557 ) -> Segment :
6658 rep : Segment = replace_class (
6759 rom_start = rom_start ,
6860 rom_end = rom_end ,
6961 type = rep_type ,
7062 name = base_name ,
7163 vram_start = vram_start ,
64+ bss_size = bss_size ,
7265 args = [],
7366 yaml = {},
7467 )
@@ -146,19 +139,22 @@ def _insert_all_auto_sections(
146139 return ret
147140
148141 def parse_subsegments (self , segment_yaml ) -> List [Segment ]:
142+ ret : List [Segment ] = []
143+
149144 if "subsegments" not in segment_yaml :
150145 if not self .parent :
151146 raise Exception (
152147 f"No subsegments provided in top-level code segment { self .name } "
153148 )
154- return []
149+ return ret
150+ yaml_subsegments = segment_yaml ["subsegments" ]
155151
156152 base_segments : OrderedDict [str , Segment ] = OrderedDict ()
157- ret : List [Segment ] = []
158- prev_start : Optional [int ] = - 1
159- prev_vram : Optional [int ] = - 1
153+ prev_start : Optional [int ] = None
154+ prev_vram : Optional [int ] = None
160155
161- last_rom_end = None
156+ # Start as the "start" address of the group.
157+ last_rom_end = self .rom_start
162158
163159 # Determine what comes first, either text or rodata/rdata
164160 readonly_before = False
@@ -172,39 +168,41 @@ def parse_subsegments(self, segment_yaml) -> List[Segment]:
172168 if rdata_index is not None :
173169 readonly_before = rdata_index < text_index
174170
175- for i , subsegment_yaml in enumerate (segment_yaml [ "subsegments" ] ):
171+ for i , subsegment_yaml in enumerate (yaml_subsegments ):
176172 # endpos marker
177173 if isinstance (subsegment_yaml , list ) and len (subsegment_yaml ) == 1 :
178174 continue
179175
176+ next_subsegment_yaml = (
177+ yaml_subsegments [i + 1 ] if i + 1 < len (yaml_subsegments ) else None
178+ )
179+
180180 typ = Segment .parse_segment_type (subsegment_yaml )
181181 start , is_auto_segment = Segment .parse_segment_start (subsegment_yaml )
182182
183183 segment_class = Segment .get_class_for_type (typ )
184184
185185 if start is None :
186- # Attempt to infer the start address
187- if i == 0 :
188- # The start address of this segment is the start address of the group
189- start = self .rom_start
190- else :
191- # The start address is the end address of the previous segment
192- start = last_rom_end
186+ # Attempt to infer the start address.
187+ # The start address is the end address of the previous segment.
188+ # If this is the first subsegment then this value will fallback
189+ # to the start address of the group.
190+ start = last_rom_end
193191
194192 # First, try to get the end address from the next segment's start address
195193 # Second, try to get the end address from the estimated size of this segment
196194 # Third, try to get the end address from the next segment with a start address
197195 end : Optional [int ] = None
198- if i < len ( segment_yaml [ "subsegments" ]) - 1 :
196+ if next_subsegment_yaml is not None :
199197 end , end_is_auto_segment = Segment .parse_segment_start (
200- segment_yaml [ "subsegments" ][ i + 1 ]
198+ next_subsegment_yaml
201199 )
202200 if start is not None and end is None :
203201 est_size = segment_class .estimate_size (subsegment_yaml )
204202 if est_size is not None :
205203 end = start + est_size
206204 if end is None :
207- end = self .get_next_seg_start (i , segment_yaml [ "subsegments" ] )
205+ end = self .get_next_seg_start (i , yaml_subsegments )
208206
209207 if start is not None and prev_start is not None and start < prev_start :
210208 log .error (
@@ -216,14 +214,25 @@ def parse_subsegments(self, segment_yaml) -> List[Segment]:
216214 assert isinstance (start , int )
217215 vram = self .get_most_parent ().rom_to_ram (start )
218216
219- if segment_class .is_noload () and last_rom_end is not None :
220- # Pretend bss's rom address is after the last actual rom segment
221- start = last_rom_end
222- # and it has a rom size of zero
223- end = last_rom_end
217+ # noload (bss) segments need a bit of special calculation
218+ start , end , vram , bss_size = self ._calculate_noload_values (
219+ segment_class ,
220+ subsegment_yaml ,
221+ next_subsegment_yaml ,
222+ start ,
223+ end ,
224+ vram ,
225+ last_rom_end ,
226+ )
224227
225- segment : Segment = Segment .from_yaml (
226- segment_class , subsegment_yaml , start , end , self , vram
228+ segment = Segment .from_yaml (
229+ segment_class ,
230+ subsegment_yaml ,
231+ start ,
232+ end ,
233+ self ,
234+ vram ,
235+ bss_size ,
227236 )
228237 segment .is_auto_segment = is_auto_segment
229238
@@ -275,6 +284,10 @@ def parse_subsegments(self, segment_yaml) -> List[Segment]:
275284
276285 return ret
277286
287+ @property
288+ def needs_symbols (self ) -> bool :
289+ return True
290+
278291 def scan (self , rom_bytes ):
279292 # Always scan code first
280293 for sub in self .subsegments :
0 commit comments