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, _bzero
41 .import __ZP_START__ ; Linker generated
42 .importzp sp, ptr1, tmp1, regbank
46 ;------------------------------------------------------------------------------
47 ; Variables stored in the register bank in the zero page. Placing the variables
48 ; here will protect them when calling other C functions.
50 Module = regbank+0 ; Pointer to module memory
51 Ctrl = regbank+2 ; Pointer to mod_ctrl structure
52 TPtr = regbank+4 ; Pointer to module data for relocation
54 ;------------------------------------------------------------------------------
59 ; Save areas and error recovery data
60 Stack: .byte 0 ; Old stackpointer
61 RegBankSave: .res 6 ; Save area for register bank
63 ; The header of the o65 file. Since we don't need the first 8 bytes any
64 ; longer, once we've checked them, we will overlay them with other data to
66 Header: .tag O65_HDR ; The o65 header
69 InputByte = Header ; Byte read from input
72 RelocVal = Header + 1 ; Relocation value
75 Read: jmp $FFFF ; Jump to read routine
79 .byte O65_MARKER_0, O65_MARKER_1 ; non C64 marker
80 .byte O65_MAGIC_0, O65_MAGIC_1, O65_MAGIC_2 ; Magic ("o65")
81 .byte O65_VERSION ; Version
82 .word O65_MODE_CC65 ; Mode word
84 ExpectedHdrSize = * - ExpectedHdr
87 ;------------------------------------------------------------------------------
88 ; PushCallerData: Push the callerdata member from control structure onto the
93 ldy #MOD_CTRL::CALLERDATA+1
100 ;------------------------------------------------------------------------------
101 ; RestoreRegBank: Restore the register bank contents from the save area. Will
102 ; destroy A and X (the latter will be zero on return).
107 @L1: lda RegBankSave-1,x
113 ;------------------------------------------------------------------------------
114 ; GetReloc: Return a relocation value based on the segment in A.
115 ; The routine uses some knowledge about the values to make the code shorter.
125 ; Text, data and bss segment
128 ldx Module+1 ; Return start address of buffer
131 ; Zero page relocation
133 @L1: lda #<__ZP_START__
137 ;------------------------------------------------------------------------------
138 ; ReadByte: Read one byte with error checking into InputByte and A.
139 ; ReadAndCheckError: Call read with the current C stack and check for errors.
147 ; C->read (C->callerdata, &B, 1)
156 ; This is a second entry point used by the other calls to Read
163 ; Check the return code and bail out in case of problems
169 @L1: lda #MLOAD_ERR_READ
174 @L2: lda InputByte ; If called ReadByte, load the byte read
177 ;------------------------------------------------------------------------------
178 ; FormatError: Bail out with an o65 format error
183 ; bne CleanupAndExit ; Branch always
185 ;------------------------------------------------------------------------------
186 ; CleanupAndExit: Free any allocated resources, restore the stack and return
192 ; Restore the stack so we may return to the caller from here
197 ; Save the error return code
201 ; Check if we have to free the allocated block
205 beq @L1 ; Jump if no memory allocated
209 jsr _free ; Free the allocated block
211 ; Restore the register bank
213 @L1: jsr RestoreRegBank
215 ; Restore the error code and return to the caller
217 ldx #$00 ; Load the high byte
221 ;------------------------------------------------------------------------------
222 ; RelocSeg: Relocate the segment pointed to by a/x
226 jsr decax1 ; Start value is segment-1
230 Loop: jsr ReadByte ; Read byte from relocation table
231 beq Done ; Bail out if end of table reached
233 cmp #255 ; Special offset?
236 ; Increment offset by 254 and continue
245 ; Increment offset by A
252 ; Read the relocation byte, extract the segment id, fetch the corresponding
253 ; relocation value and place it into ptr1
261 ; Get the relocation byte again, this time extract the relocation type.
266 ; Check for and handle the different relocation types.
275 ; Relocate the low byte
283 ; Relocate a high byte
286 jsr ReadByte ; Read low byte from relocation table
289 adc RelocVal ; We just need the carry
295 jmp Loop ; Done, next entry
306 bne AddHigh ; Branch always (add high byte)
308 ;------------------------------------------------------------------------------
309 ; mod_load: Load and relocate an o65 module
314 ; Save the register bank and clear the Module pointer
326 ; Save the passed parameter
331 ; Save the stack pointer so we can bail out even from subroutines
336 ; Get the read function pointer from the control structure and place it into
346 ; Read the o65 header: C->read (C->callerdata, &H, sizeof (H))
352 lda #.sizeof(O65_HDR)
353 ldx #0 ; Always less than 256
354 jsr ReadAndCheckError ; Bails out in case of errors
356 ; We read the o65 header successfully. Validate it.
358 ldy #ExpectedHdrSize-1
366 ; Header is ok as far as we can say now. Read all options, check for the
367 ; OS option and ignore all others. The OS option contains a version number
368 ; and the module id as additional data.
371 sty TPtr+1 ; Flag for OS option read
372 Opt: jsr ReadByte ; Read the length byte
373 beq OptDone ; Jump if done
374 sta TPtr ; Use TPtr as a counter
376 ; An option has a length of at least 2 bytes
379 bcc HeaderError ; Must be 2 bytes total at least
381 ; Check for the OS option
384 jsr ReadByte ; Get the option type
385 cmp #O65_OPT_OS ; OS option?
386 bne SkipOpt ; No: Skip
388 lda TPtr ; Get remaining length+1
389 cmp #5 ; CC65 has 6 bytes total
392 jsr ReadByte ; Get the operating system
393 cmp #O65_OS_CC65_MODULE
394 bne OSError ; Wrong operating system
396 jsr ReadByte ; Get the version number, expect zero
397 bne OSError ; Wrong version
399 jsr ReadByte ; Get low byte of id
400 ldy #MOD_CTRL::MODULE_ID
403 ldy #MOD_CTRL::MODULE_ID+1
406 inc TPtr+1 ; Remember that we got the OS
414 beq Opt ; Next option
415 jsr ReadByte ; Skip one byte
418 ; Operating system error
424 ; Options done, check that we got the OS option
430 ; Entry point for header errors
436 ; Skipped all options. Calculate the size of text+data and of text+data+bss
437 ; (the latter is the size of the memory block we need). We will store the
438 ; total module size also into the control structure for evaluation by the
442 lda Header + O65_HDR::TLEN
443 add Header + O65_HDR::DLEN
445 lda Header + O65_HDR::TLEN + 1
446 adc Header + O65_HDR::DLEN + 1
449 add Header + O65_HDR::BLEN
450 pha ; Save low byte of total size
451 ldy #MOD_CTRL::MODULE_SIZE
454 adc Header + O65_HDR::BLEN + 1
458 pla ; Restore low byte of total size
460 ; Total memory size is now in a/x. Allocate memory and remember the result,
461 ; both, locally and in the control structure so it the caller can access
462 ; the memory block. After that, check if we got the requested memory.
468 ldy #MOD_CTRL::MODULE
476 ; Could not allocate memory
481 ; Control structure is complete now. Clear the bss segment.
482 ; bzero (bss_addr, bss_size)
488 adc TPtr+1 ; Module + tlen + dlen
492 lda Header + O65_HDR::BLEN
493 ldx Header + O65_HDR::BLEN+1
494 jsr _bzero ; bzero (bss, bss_size);
496 ; Load code and data segment into memory. The sum of the sizes of
497 ; code+data segment is still in TPtr.
498 ; C->read (C->callerdata, C->module, H.tlen + H.dlen)
506 jsr ReadAndCheckError ; Bails out in case of errors
508 ; We've got the code and data segments in memory. Next section contains
509 ; undefined references which we don't support. So check if the count of
510 ; undefined references is actually zero.
516 Undef: jmp FormatError
518 ; Number of undefined references was zero. Next come the relocation tables
519 ; for code and data segment. Relocate the code segment
522 ldx Module + 1 ; Code segment address
525 ; Relocate the data segment
528 add Header + O65_HDR::TLEN
531 adc Header + O65_HDR::TLEN + 1
533 pla ; Data segment address in a/x
536 ; We're done. Restore the register bank and return a success code
538 jsr RestoreRegBank ; X will be zero on return