6502 C-Compiler cc65

OP #7883398
Lesenswert?

Im Moment versuche ich gerade, Assemblercode für den Junior-Computer mit dem C-Compiler zu erzeugen.

1
[
2
/*
3
 * Blinking an LED connected to PORTB0 of a Junior Computer
4
 *
5
 * compile blink.c
6

7
 *
8
 * 2025-05-28 mchris
9
 *
10
 */
11

12
// MOS6532 PIA addrese $1A80
13
#define PBD             (*(volatile unsigned char*)0x1A82)
14
#define PBDD            (*(volatile unsigned char*)0x1A83)
15
#define TIMER_READ      (*(volatile unsigned char*)0x1A84)
16
#define TIMER_DIV1024   (*(volatile unsigned char*)0x1A97)
17

18
void blink(void)
19
{
20
    // init PORTB0 as output
21
    PBDD=1;
22
    while(1)
23
    {
24
        TIMER_DIV1024=248;      // init timer for clkdiv=1024 and start=248
25
        while(TIMER_READ!=0);   // wait until timer is zero
26
        PBD^=1;                 ^// toggle PORTB0
27
    }
28
}

Mit der Befehlszeile
cc65 -O -t none blink.c
läst sich das folgende Assembler-File erzeugen:

1
;
2
; File generated by cc65 v 2.18 
3
;
4
  .fopt    compiler,"cc65 v 2.18 "
5
  .setcpu    "6502"
6
  .smart    on
7
  .autoimport  on
8
  .case    on
9
  .debuginfo  off
10
  .importzp  sp, sreg, regsave, regbank
11
  .importzp  tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
12
  .macpack  longbranch
13
  .export    _blink
14

15
; ---------------------------------------------------------------
16
; void __near__ blink (void)
17
; ---------------------------------------------------------------
18

19
.segment  "CODE"
20

21
.proc  _blink: near
22

23
.segment  "CODE"
24

25
  lda     #$01
26
  sta     $1A83
27
L0014:  lda     #$F8
28
  sta     $1A97
29
L0015:  lda     $1A84
30
  bne     L0015
31
  lda     $1A82
32
  eor     #$01
33
  sta     $1A82
34
  jmp     L0014
35

36
.endproc

Was gar nicht so schlecht aussieht, wenn man es mit dem originale Assemblerfile, anhand dessen der C-Code erstellt wurde, vergleicht (siehe Anhang)

Das Problem: wie kann man aus diesem Assemblerfile den Binärcode erstellen, so dass er an der Adresse 0x300 beginnt?

Links: cc65 : https://cc65.github.io/

Junior Computer: http://retro.hansotten.nl/6502-sbc/elektuur-junior/junior/base-elektor-junior/

Angehängte Dateien:
OP #7883410
Lesenswert?

Andras H. (andras_h) 28.05.2025 16:45

Fehlt da nicht noch ein main und der C-Startup davor? Oder wie soll die C Umgebung aufgesetzt werden?

Denke ich auch. Aber es werden ja die gleichen Befehle wie im oben angehängten 'Original' Assemblerprogramm (blink.asm) erzeugt und das braucht ja auch keinen Startup-Code.

#7883417
Lesenswert?

Na ja, ich würde trozdem über den main laufen wollen. Vor allem, wenn der Compiler richtig den CPU supported, dann ist da schon alles fertig, und man hat später alle vorteile (?). Es wird auch einfacher zu testen.

Sonst Compiler Manual aufmachen und gucken, ob man irgendwie handgeschriebene asm befehle reintun kann. Meisst geht das mit asm("") oder _asm. Damit kann man beliebig hacken.

Aber ich würde erstmals den Hello Word empfehlen. Damit man guckt ob die Umgebung richtig funktioniert. Wenn das geht, dann würde ich versuchen irgendwie davon abweichen. Zum beispiel etwas ohne den startup code zu bauen.

#7883443
Lesenswert?

Beim cc65: nach dem Compile muss erst der Assembler Und dann der Linker mit einem config-File aufgerufen werden: In etwa so:

cc65 -O -t none blink.c ca65 -l -cpu 65sc02 blink.s ld65 -C deinesystemconfig.cfg blink.o -o blink.out

Das Config-File "deinesystemconfig.cfg" muss auf dein System passen, Beispiele in "deinPfadzumcc65/lib/cc65/cfg" ...

Config-File Doku: https://cc65.github.io/doc/ld65.html#s5

OP #7883639
Lesenswert?

Bernhard K. (bkom) 28.05.2025 17:48

cc65 -O -t none blink.c ca65 -l -cpu 65sc02 blink.s ld65 -C deinesystemconfig.cfg blink.o -o blink.out

Danke für den Hinweis.
Wie so oft, liegen die Probleme in den "filigranen" Details und mag an meiner cc65-Version
**ca65 V2.18 - Ubuntu 2.19-1
liegen, die standardmäßig im Ubuntu-Repo zu finden ist. Wenn ich in das Github-Repo des cc65 schaue, ist da ja eine ständige Weiterentwicklung zu sehen:
https://github.com/cc65/cc65

Mein Befehlssequenz sieht jetzt folgendermaßen aus (cfg und startup file im Anhang):

1
# C-Source nach Assembler konvertieren
2
cc65 -O -t none blink.c
3

4
# Assembler file compilieren
5
ca65 -l blink.lst blink.s
6
#( Listfile -l braucht filnamen )
7

8
#Startup script wird gebraucht und muss compiliert werden
9
cl65 -t none -c crt0.s -o crt0.o
10

11
# alles zusammenbauen: Link startup blink und startup-Code
12
ld65 -C junior.cfg crt0.o blink.o -o blink.bin

Aber es darf ja nichts zu einfach sein: Im Startup-Code muss ich das 'jsr main' auskommentieren. Aus irgendwelchen Gründen wird das 'main' nicht gefunden ..

Angehängte Dateien:
#7883765
Lesenswert?

Christoph M. schrieb:

Ersetzte den Funktionsnamen 'blink' durch 'main' und es wird trotzdem nicht gehen.

Aus dem main() im .c-source wird beim compilieren im .s ein _main exportiert, warum auch immer - ich kenne mich damit nicht aus. Das auskommentieren von jsr main würde dazu führten, dass blink nie ausgeführt wird. Stattdessen könnte man im crt.s main zu _main ändern.

OP #7883816
Lesenswert?

Klaus (feelfree) 29.05.2025 12:54

Aus dem main() im .c-source wird beim compilieren im .s ein _main exportiert, warum auch immer

Das sind die 'filigranen' Details, von denen ich oben geschrieben hatte. Ein kleiner Unterstrich "_" macht den Unterschied, ob es geht oder nicht geht.
Mit dem Unterstrich kann man das erste Beispiel kompilieren .. ein erster Erfolg.

Aber die Welt darf ja nicht zu einfach sein. Will ich das Tonleiterprogramm kompilieren, welches Funktionsaufrufe aus der 'main' enthält

1
#include <stdint.h>
2

3
#define PBD             (*(volatile unsigned char*)0x1A82)
4
#define PBDD            (*(volatile unsigned char*)0x1A83)
5

6
void shortdelay(uint8_t x)
7
{
8
    do
9
    {
10
        x--;
11
    }while(x!=0);
12
}
13

14
void playnote(uint8_t n)
15
{
16
    uint8_t duration=50;
17

18
    do
19
    {
20
        shortdelay(n);
21
        shortdelay(0);
22

23
        PBD^=1;                 // toggle PORTB0
24

25
        duration--;
26
    }while(duration!=0);
27
}
28

29
void main(void)
30
{
31
    // init PORTB0 as output
32
    PBDD=1;
33
    while(1)
34
    {
35
        playnote(200);
36
        playnote(158);
37
    }
38
}

Werden die Stack Funktionen des C-Systems vermisst.

1
Linking temp.o and crt0.o, creating temp.bin
2
Unresolved external 'incsp1' referenced in:
3
  temp.s(36)
4
Unresolved external 'incsp2' referenced in:
5
  temp.s(68)
6
Unresolved external 'pusha' referenced in:
7
  temp.s(28)
8
  temp.s(50)
9
  temp.s(52)
10
Unresolved external 'sp' referenced in:
11
  temp.s(54)
12
  temp.s(62)
13
  temp.s(65)
14
  temp.s(66)
15
  temp.s(30)
16
  temp.s(33)
17
  temp.s(34)

Es fehlt als das nächste Puzzelstück im großen Kompiler-Abenteuerspiel.

Gast #7883827
Lesenswert?

Klaus schrieb:

Christoph M. schrieb:

Aber die Welt darf ja nicht zu einfach sein.

Nun ja, wer für einen 45 Jahre alten Einplatinenrechner ohne vernünftige Ein/Ausgabemöglichkeiten plötzlich eine Toolchain incl. C-Compiler haben will, kann halt nicht davon ausgehen, dass vor ihm schonmal jemand so verrrückt war, das auszuprobieren ...

Und dann nicht auf richtiger Hardware sondern mit einem Emulator entwickelt: That's the new Frontier!

P.S.: Genial finde ich es trotzdem.

#7883841
Lesenswert?

alles zusammenbauen: Link startup blink und startup-Code

ld65 -C junior.cfg crt0.o blink.o -o blink.bin

[/c]

Ich erinnere mich kaum noch - ist schon mehr als 15 Jahre her - aber ich meine es fehlt noch "-t junior", die runtime-lib für den Juniorcomputer gibts wahrscheinlich nicht, "-t kim" könnte aber auch funktionieren ...

#7883847
Lesenswert?

Bernhard K. schrieb:

alles zusammenbauen: Link startup blink und startup-Code

ld65 -C junior.cfg crt0.o blink.o -o blink.bin

[/c]

Ich erinnere mich kaum noch - ist schon mehr als 15 Jahre her - aber ich meine es fehlt noch "-t junior", die runtime-lib für den Juniorcomputer gibts wahrscheinlich nicht, "-t kim" könnte aber auch funktionieren ...

1
ld65 -t none crt0.o playnote.o none.lib -o playnote.bin

macht den Trick.

OP #7884048
Lesenswert?

Klaus (feelfree) 29.05.2025 16:30

1
ld65 -t none crt0.o playnote.o none.lib -o playnote.bin

macht den Trick.

Jetzt scheint der Compiler das Linker-File zu ignorieren und statt das main an der gewünschten Adresse 0x300 anzuspringen, statt dessen nach 0x1000 springt.

Mein Konfigurationsfile sieht so aus ( beim STACKSTART bin ich mir noch unsicher )

1
SYMBOLS {
2
    __STARTUP__:   type = import;
3
    __STACKSIZE__: type = weak, value = $0080;
4
    __STACKSTART__: type = weak, value = $0180;
5
}
6
MEMORY {
7
    ZP:       file = "", define = yes, start = $0000, size = $0100;
8
    CPUSTACK: file = "",               start = $0100, size = $0100;
9
    RAM:      file = %O, define = yes, start = $0300, size = $0800 - __STACKSIZE__;
10
}
11
SEGMENTS {
12
    ZEROPAGE: load = ZP,     type = zp;
13
    STARTUP:  load = RAM,    type = ro;
14
    LOWCODE:  load = RAM,    type = ro,  optional = yes;
15
    ONCE:     load = RAM,    type = ro,  optional = yes;
16
    CODE:     load = RAM,    type = ro;
17
    RODATA:   load = RAM,    type = ro;
18
    DATA:     load = RAM,    type = rw,  define   = yes;
19
    BSS:      load = RAM,    type = bss, define   = yes;
20
}
21
FEATURES {
22
    CONDES: type    = constructor,
23
            label   = __CONSTRUCTOR_TABLE__,
24
            count   = __CONSTRUCTOR_COUNT__,
25
            segment = ONCE;
26
    CONDES: type    = destructor,
27
            label   = __DESTRUCTOR_TABLE__,
28
            count   = __DESTRUCTOR_COUNT__,
29
            segment = RODATA;
30
    CONDES: type    = interruptor,
31
            label   = __INTERRUPTOR_TABLE__,
32
            count   = __INTERRUPTOR_COUNT__,
33
            segment = RODATA,
34
            import  = __CALLIRQ__;
35
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren