1 ;*****************************************************************************/
5 ;* o65 module loader for the cc65 library */
9 ;* (C) 2002 Ullrich von Bassewitz */
11 ;* D-70597 Stuttgart */
12 ;* EMail: uz@musoftware.de */
15 ;* This software is provided 'as-is', without any expressed or implied */
16 ;* warranty. In no event will the authors be held liable for any damages */
17 ;* arising from the use of this software. */
19 ;* Permission is granted to anyone to use this software for any purpose, */
20 ;* including commercial applications, and to alter it and redistribute it */
21 ;* freely, subject to the following restrictions: */
23 ;* 1. The origin of this software must not be misrepresented; you must not */
24 ;* claim that you wrote the original software. If you use this software */
25 ;* in a product, an acknowledgment in the product documentation would be */
26 ;* appreciated but is not required. */
27 ;* 2. Altered source versions must be plainly marked as such, and must not */
28 ;* be misrepresented as being the original software. */
29 ;* 3. This notice may not be removed or altered from any source */
32 ;*****************************************************************************/
37 .include "modload.inc"
39 .import pushax, pusha0, push0, push1, decax1
40 .import _malloc, _free, _memset
41 .importzp sp, ptr1, tmp1, regbank
45 ;------------------------------------------------------------------------------
46 ; Variables stored in the register bank in the zero page. Placing the variables
47 ; here will protect them when calling other C functions.
49 Module = regbank+0 ; Pointer to module memory
50 Ctrl = regbank+2 ; Pointer to mod_ctrl structure
51 TPtr = regbank+4 ; Pointer to module data for relocation
53 ;------------------------------------------------------------------------------
58 ; Save areas and error recovery data
59 Stack: .byte 0 ; Old stackpointer
60 RegBankSave: .res 6 ; Save area for register bank
62 ; The header of the o65 file. Since we don't need the 8 bytes any longer,
63 ; once we've checked them, we will overlay them with other data to save a
65 Header: .res O65_HDR_SIZE ; The o65 header
68 InputByte = Header ; Byte read from input
70 ; Stuff needed for relocation
71 TextReloc = Header + 1 ; Relocation value for code seg
72 DataReloc = Header + 3 ; Relocation value for data seg
73 BssReloc = Header + 5 ; Relocation value for bss seg
76 Read: jmp $FFFF ; Jump to read routine
79 ExpectedHdr: .byte $01, $00 ; non C64 marker
80 .byte $6F, $36, $35 ; Magic ("o65")
82 .word $0000 ; Mode word
83 ExpectedHdrSize = * - ExpectedHdr
86 ;------------------------------------------------------------------------------
87 ; PushCtrl: Push the address of the control structure onto the C stack.
95 ;------------------------------------------------------------------------------
96 ; LoadCtrl: Load a word from the control structure into a/x. The offset of the
97 ; high byte is passed in Y.
107 ;------------------------------------------------------------------------------
108 ; RestoreRegBank: Restore the register bank contents from the save area. Will
114 @L1: lda RegBankSave,x
120 ;------------------------------------------------------------------------------
121 ; GetReloc: Return a relocation value based on the segment in A
131 @L1: cmp #O65_SEGID_DATA
137 @L2: cmp #O65_SEGID_BSS
143 @L3: lda #MLOAD_ERR_FMT
144 ; bne CleanupAndExit ; Branch always
146 ;------------------------------------------------------------------------------
147 ; CleanupAndExit: Free any allocated resources, restore the stack and return
153 ; Restore the stack so we may return to the caller from here
158 ; Save the error return code
162 ; Check if we have to free the allocated block
166 beq @L1 ; Jump if no memory allocated
170 jsr _free ; Free the allocated block
172 ; Restore the register bank
174 @L1: jsr RestoreRegBank
176 ; Restore the error code and return to the caller
178 ldx #$00 ; Load the high byte
182 ;------------------------------------------------------------------------------
183 ; ReadByte: Read one byte with error checking into InputByte and A.
197 ; Check the return code and bail out in case of problems
209 ;------------------------------------------------------------------------------
210 ; RelocSeg: Relocate the segment pointed to by a/x
214 jsr decax1 ; Start value is segment-1
218 Loop: jsr ReadByte ; Read byte from relocation table
219 beq Done ; Bail out if end of table reached
221 cmp #255 ; Special offset?
224 ; Increment offset by 254 and continue
233 ; Increment offset by A
240 ; Read the relocation byte, extract the segment id, fetch the corresponding
241 ; relocation value and place it into ptr1
249 ; Get the relocation byte again, this time extract the relocation type.
254 ; Check for and handle the different relocation types.
264 bne @L4 ; Branch always (add high byte)
266 @L3: cmp #O65_RTYPE_HIGH
268 jsr ReadByte ; Read low byte from relocation table
271 adc ptr1 ; We just need the carry
275 jmp Loop ; Done, next entry
277 @L5: cmp #O65_RTYPE_LOW
280 ; Problem: Invalid relocation code
291 ; Finished with this relocation
295 ;------------------------------------------------------------------------------
296 ; mod_load: Load and relocate an o65 module
301 ; Save the register bank and clear the Module pointer
313 ; Save the passed parameter
318 ; Save the stack pointer so we can bail out even from subroutines
323 ; Get the read function pointer from the control structure and place it into
331 ; Read the o65 header: C->read (C, &H, sizeof (H))
338 jsr pusha0 ; Always less than 256
341 ; Check the return code
348 ; We read the o65 header successfully. Validate it.
350 @L2: ldy #ExpectedHdrSize-1
359 ; Header is ok as far as we can say now. Read and skip all options. We may
360 ; add a check here for the OS option later.
363 beq OptDone ; Jump if done
364 sta TPtr ; Use TPtr as a counter
366 beq Opt ; Next option
367 jsr ReadByte ; Skip one byte
371 ; Skipped all options. Calculate the sizes of several areas needed later
373 lda Header + O65_HDR_TLEN
374 add Header + O65_HDR_DLEN
376 lda Header + O65_HDR_TLEN + 1
377 adc Header + O65_HDR_DLEN + 1
380 add Header + O65_HDR_BLEN
383 adc Header + O65_HDR_BLEN + 1
385 ; Load the total module size into a/x and store it into the control structure
387 ldy #MODCTRL_MODULE_SIZE + 1
394 ; Allocate memory, check if we got it
402 ; Could not allocate memory
407 ; We got the memory block. Setup the pointers and sizes in the control
408 ; structure. We will use internal knowlege about the layout of the structure
409 ; here to save some code.
418 sta (Ctrl),y ; MODCTRL_CODE+1
419 ldy #MODCTRL_MODULE+1
422 ; The following loop will also copy some information that is not needed just
426 ldy #MODCTRL_CODE_SIZE
434 ; Missing in the control structure now: start of the data and bss segments
438 add Header + O65_HDR_TLEN
442 adc Header + O65_HDR_TLEN + 1
454 ; Control structure is complete now. Load code and data segment into memory.
455 ; The sum of the sizes of code and data segment is still in TPtr.
456 ; C->read (C, C->module, H.tlen + H.dlen)
474 ; We've got the code and data segments in memory. Next section contains
475 ; undefined references which we don't support. So check if the count of
476 ; undefined references is actually zero.
482 Undef: lda #MLOAD_ERR_FMT
485 ; Number of undefined references was zero. Next sections are the relocation
486 ; tables for code and data segment. Before doing the actual relocation, we
487 ; have to setup the relocation values for the three segments.
490 sub Header + O65_HDR_TBASE
493 sbc Header + O65_HDR_TBASE + 1
498 sub Header + O65_HDR_DBASE
502 sbc Header + O65_HDR_DBASE + 1
507 sub Header + O65_HDR_BBASE
511 sbc Header + O65_HDR_BBASE + 1
514 ; Relocate the code segment
517 ldx Module + 1 ; Code segment address
520 ; Relocate the data segment
522 ldy #MODCTRL_DATA + 1
523 jsr LoadCtrl ; Get data segment address
526 ; Clear the bss segment
529 jsr LoadCtrl ; Load bss segment address
532 ldy #MODCTRL_BSS_SIZE + 1
533 jsr LoadCtrl ; Load bss segment size
534 jsr _memset ; memset (bss, 0, bss_size);
536 ; We're done. Restore the register bank and return a success code