File: modload.s

package info (click to toggle)
cc65 2.19-2
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 20,268 kB
  • sloc: ansic: 117,151; asm: 66,339; pascal: 4,248; makefile: 1,009; perl: 607
file content (540 lines) | stat: -rw-r--r-- 16,439 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
;*****************************************************************************/
;*                                                                           */
;*                                   modload.s                               */
;*                                                                           */
;*                    o65 module loader for the cc65 library                 */
;*                                                                           */
;*                                                                           */
;*                                                                           */
;* (C) 2002      Ullrich von Bassewitz                                       */
;*               Wacholderweg 14                                             */
;*               D-70597 Stuttgart                                           */
;* EMail:        uz@musoftware.de                                            */
;*                                                                           */
;*                                                                           */
;* This software is provided 'as-is', without any expressed or implied       */
;* warranty.  In no event will the authors be held liable for any damages    */
;* arising from the use of this software.                                    */
;*                                                                           */
;* Permission is granted to anyone to use this software for any purpose,     */
;* including commercial applications, and to alter it and redistribute it    */
;* freely, subject to the following restrictions:                            */
;*                                                                           */
;* 1. The origin of this software must not be misrepresented; you must not   */
;*    claim that you wrote the original software. If you use this software   */
;*    in a product, an acknowledgment in the product documentation would be  */
;*    appreciated but is not required.                                       */
;* 2. Altered source versions must be plainly marked as such, and must not   */
;*    be misrepresented as being the original software.                      */
;* 3. This notice may not be removed or altered from any source              */
;*    distribution.                                                          */
;*                                                                           */
;*****************************************************************************/



        .include        "o65.inc"
        .include        "modload.inc"
        .include        "zeropage.inc"

        .import         pushax, pusha0, push0, push1, decax1
        .import         _malloc, _free, _bzero
        .import         __ZP_START__    ; Linker generated

        .macpack        generic

;------------------------------------------------------------------------------
; Variables stored in the register bank in the zero page. Placing the variables
; here will protect them when calling other C functions.

Module          = regbank+0             ; Pointer to module memory
Ctrl            = regbank+2             ; Pointer to mod_ctrl structure
TPtr            = regbank+4             ; Pointer to module data for relocation

;------------------------------------------------------------------------------
; Static module data

.bss

; Save areas and error recovery data
Stack:          .byte   0               ; Old stackpointer
RegBankSave:    .res    regbanksize     ; Save area for register bank

; The header of the o65 file. Since we don't need the first 8 bytes any
; longer, once we've checked them, we will overlay them with other data to
; save a few bytes.
Header:         .tag    O65_HDR         ; The o65 header

; Input
InputByte       = Header                ; Byte read from input

; Relocation
RelocVal        = Header + 1            ; Relocation value

.data
Read:   jmp     $FFFF                   ; Jump to read routine

.rodata
ExpectedHdr:
        .byte   O65_MARKER_0, O65_MARKER_1              ; non C64 marker
        .byte   O65_MAGIC_0, O65_MAGIC_1, O65_MAGIC_2   ; Magic ("o65")
        .byte   O65_VERSION                             ; Version
        .word   O65_MODE_CC65                           ; Mode word

ExpectedHdrSize = * - ExpectedHdr


;------------------------------------------------------------------------------
; PushCallerData: Push the callerdata member from control structure onto the
; C stack.

.code
PushCallerData:
        ldy     #MOD_CTRL::CALLERDATA+1
        lda     (Ctrl),y
        tax
        dey
        lda     (Ctrl),y
        jmp     pushax

;------------------------------------------------------------------------------
; RestoreRegBank: Restore the register bank contents from the save area. Will
;                 destroy A and X (the latter will be zero on return).

.code
RestoreRegBank:
        ldx     #6
@L1:    lda     RegBankSave-1,x
        sta     regbank-1,x
        dex
        bne     @L1
        rts

;------------------------------------------------------------------------------
; GetReloc: Return a relocation value based on the segment in A.
; The routine uses some knowledge about the values to make the code shorter.

.code
GetReloc:
        cmp     #O65_SEGID_TEXT
        bcc     FormatError
        cmp     #O65_SEGID_ZP
        beq     @L1
        bcs     FormatError

; Text, data and bss segment

        lda     Module
        ldx     Module+1                ; Return start address of buffer
        rts

; Zero page relocation

@L1:    lda     #<__ZP_START__
        ldx     #>__ZP_START__
        rts

;------------------------------------------------------------------------------
; ReadByte: Read one byte with error checking into InputByte and A.
; ReadAndCheckError: Call read with the current C stack and check for errors.

.bss
ReadSize:       .res    2

.code
ReadByte:

; C->read (C->callerdata, &B, 1)

        jsr     PushCallerData
        lda     #<InputByte
        ldx     #>InputByte
        jsr     pushax
        ldx     #0
        lda     #1

; This is a second entry point used by the other calls to Read

ReadAndCheckError:
        sta     ReadSize
        stx     ReadSize+1
        jsr     Read

; Check the return code and bail out in case of problems

        cmp     ReadSize
        bne     @L1
        cpx     ReadSize+1
        beq     @L2                     ; Jump if ok
@L1:    lda     #MLOAD_ERR_READ
        bne     CleanupAndExit

; Done

@L2:    lda     InputByte               ; If called ReadByte, load the byte read
Done:   rts

;------------------------------------------------------------------------------
; FormatError: Bail out with an o65 format error

.code
FormatError:
        lda     #MLOAD_ERR_FMT
;       bne     CleanupAndExit          ; Branch always

;------------------------------------------------------------------------------
; CleanupAndExit: Free any allocated resources, restore the stack and return
;                 to the caller.

.code
CleanupAndExit:

; Restore the stack so we may return to the caller from here

        ldx     Stack
        txs

; Save the error return code

        pha

; Check if we have to free the allocated block

        lda     Module
        ldx     Module+1
        bne     @L1
        tay                             ; Test high byte
        beq     @L2
@L1:    jsr     _free                   ; Free the allocated block

; Restore the register bank

@L2:    jsr     RestoreRegBank

; Restore the  error code and return to the caller

        ldx     #$00                    ; Load the high byte
        pla
        rts

;------------------------------------------------------------------------------
; RelocSeg: Relocate the segment pointed to by a/x

.code
RelocSeg:
        jsr     decax1                  ; Start value is segment-1
        sta     TPtr
        stx     TPtr+1

Loop:   jsr     ReadByte                ; Read byte from relocation table
        beq     Done                    ; Bail out if end of table reached

        cmp     #255                    ; Special offset?
        bne     @L1

; Increment offset by 254 and continue

        lda     TPtr
        add     #254
        sta     TPtr
        bcc     Loop
        inc     TPtr+1
        jmp     Loop

; Increment offset by A

@L1:    add     TPtr
        sta     TPtr
        bcc     @L2
        inc     TPtr+1

; Read the relocation byte, extract the segment id, fetch the corresponding
; relocation value and place it into ptr1

@L2:    jsr     ReadByte
        and     #O65_SEGID_MASK
        jsr     GetReloc
        sta     RelocVal
        stx     RelocVal+1

; Get the relocation byte again, this time extract the relocation type.

        lda     InputByte
        and     #O65_RTYPE_MASK

; Check for and handle the different relocation types.

        cmp     #O65_RTYPE_WORD
        beq     RelocWord
        cmp     #O65_RTYPE_HIGH
        beq     RelocHigh
        cmp     #O65_RTYPE_LOW
        bne     FormatError

; Relocate the low byte

RelocLow:
        ldy     #0
        clc
        lda     RelocVal
        bcc     AddCommon

; Relocate a high byte

RelocHigh:
        jsr     ReadByte                ; Read low byte from relocation table
        ldy     #0
        clc
        adc     RelocVal                ; We just need the carry
AddHigh:
        lda     RelocVal+1
AddCommon:
        adc     (TPtr),y
        sta     (TPtr),y
        jmp     Loop                    ; Done, next entry

; Relocate a word

RelocWord:
        ldy     #0
        clc
        lda     RelocVal
        adc     (TPtr),y
        sta     (TPtr),y
        iny
        bne     AddHigh                 ; Branch always (add high byte)

;------------------------------------------------------------------------------
; mod_load: Load and relocate an o65 module

.code
_mod_load:

; Save the register bank and clear the Module pointer

        pha
        ldy     #6
@L1:    lda     regbank-1,y
        sta     RegBankSave-1,y
        dey
        bne     @L1
        sty     Module
        sty     Module+1
        pla

; Save the passed parameter

        sta     Ctrl
        stx     Ctrl+1

; Save the stack pointer so we can bail out even from subroutines

        tsx
        stx     Stack

; Get the read function pointer from the control structure and place it into
; our call vector

        ldy     #MOD_CTRL::READ
        lda     (Ctrl),y
        sta     Read+1
        iny
        lda     (Ctrl),y
        sta     Read+2

; Read the o65 header: C->read (C->callerdata, &H, sizeof (H))

        jsr     PushCallerData
        lda     #<Header
        ldx     #>Header
        jsr     pushax
        lda     #.sizeof(O65_HDR)
        ldx     #0                      ; Always less than 256
        jsr     ReadAndCheckError       ; Bails out in case of errors

; We read the o65 header successfully. Validate it.

        ldy     #ExpectedHdrSize-1
ValidateHeader:
        lda     Header,y
        cmp     ExpectedHdr,y
        bne     HeaderError
        dey
        bpl     ValidateHeader

; Header is ok as far as we can say now. Read all options, check for the
; OS option and ignore all others. The OS option contains a version number
; and the module id as additional data.

        iny                             ; Y = $00
        sty     TPtr+1                  ; Flag for OS option read
Opt:    jsr     ReadByte                ; Read the length byte
        beq     OptDone                 ; Jump if done
        sta     TPtr                    ; Use TPtr as a counter

; An option has a length of at least 2 bytes

        cmp     #2
        bcc     HeaderError             ; Must be 2 bytes total at least

; Check for the OS option

        dec     TPtr
        jsr     ReadByte                ; Get the option type
        cmp     #O65_OPT_OS             ; OS option?
        bne     SkipOpt                 ; No: Skip

        lda     TPtr                    ; Get remaining length+1
        cmp     #5                      ; CC65 has 6 bytes total
        bne     OSError

        jsr     ReadByte                ; Get the operating system
        cmp     #O65_OS_CC65
        bne     OSError                 ; Wrong operating system

        jsr     ReadByte                ; Get the version number, expect zero
        bne     OSError                 ; Wrong version

        jsr     ReadByte                ; Get low byte of id
        ldy     #MOD_CTRL::MODULE_ID
        sta     (Ctrl),y
        jsr     ReadByte
        ldy     #MOD_CTRL::MODULE_ID+1
        sta     (Ctrl),y

        inc     TPtr+1                  ; Remember that we got the OS

        jmp     Opt

; Skip one option

SkipOpt:
        dec     TPtr
        beq     Opt                     ; Next option
        jsr     ReadByte                ; Skip one byte
        jmp     SkipOpt

; Operating system error

OSError:
        lda     #MLOAD_ERR_OS
        jmp     CleanupAndExit

; Options done, check that we got the OS option

OptDone:
        lda     TPtr+1
        bne     CalcSizes

; Entry point for header errors

HeaderError:
        lda     #MLOAD_ERR_HDR
        jmp     CleanupAndExit

; Skipped all options. Calculate the size of text+data and of text+data+bss
; (the latter is the size of the memory block we need). We will store the
; total module size also into the control structure for evaluation by the
; caller

CalcSizes:
        lda     Header + O65_HDR::TLEN
        add     Header + O65_HDR::DLEN
        sta     TPtr
        lda     Header + O65_HDR::TLEN + 1
        adc     Header + O65_HDR::DLEN + 1
        sta     TPtr+1
        lda     TPtr
        add     Header + O65_HDR::BLEN
        pha                             ; Save low byte of total size
        ldy     #MOD_CTRL::MODULE_SIZE
        sta     (Ctrl),y
        lda     TPtr+1
        adc     Header + O65_HDR::BLEN + 1
        iny
        sta     (Ctrl),y
        tax
        pla                             ; Restore low byte of total size

; Total memory size is now in a/x. Allocate memory and remember the result,
; both, locally and in the control structure so it the caller can access
; the memory block. After that, check if we got the requested memory.

        jsr     _malloc
        sta     Module
        stx     Module+1

        ldy     #MOD_CTRL::MODULE
        sta     (Ctrl),y
        txa
        iny
        sta     (Ctrl),y
        ora     Module
        bne     GotMem

; Could not allocate memory

        lda     #MLOAD_ERR_MEM
        jmp     CleanupAndExit

; Control structure is complete now. Clear the bss segment.
; bzero (bss_addr, bss_size)

GotMem: lda     Module
        add     TPtr
        pha
        lda     Module+1
        adc     TPtr+1                  ; Module + tlen + dlen
        tax
        pla
        jsr     pushax
        lda     Header + O65_HDR::BLEN
        ldx     Header + O65_HDR::BLEN+1
        jsr     _bzero                  ; bzero (bss, bss_size);

; Load code and data segment into memory. The sum of the sizes of
; code+data segment is still in TPtr.
; C->read (C->callerdata, C->module, H.tlen + H.dlen)

        jsr     PushCallerData
        lda     Module
        ldx     Module+1
        jsr     pushax
        lda     TPtr
        ldx     TPtr+1
        jsr     ReadAndCheckError       ; Bails out in case of errors

; We've got the code and data segments in memory. Next section contains
; undefined references which we don't support. So check if the count of
; undefined references is actually zero.

        jsr     ReadByte
        bne     Undef
        jsr     ReadByte
        beq     Reloc
Undef:  jmp     FormatError

; Number of undefined references was zero. Next come the relocation tables
; for code and data segment. Relocate the code segment

Reloc:  lda     Module
        ldx     Module + 1              ; Code segment address
        jsr     RelocSeg

; Relocate the data segment

        lda     Module
        add     Header + O65_HDR::TLEN
        pha
        lda     Module + 1
        adc     Header + O65_HDR::TLEN + 1
        tax
        pla                             ; Data segment address in a/x
        jsr     RelocSeg

; We're done. Restore the register bank and return a success code

        jsr     RestoreRegBank          ; X will be zero on return
        lda     #MLOAD_OK
        rts