6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Apr 27, 2024 7:45 pm

All times are UTC




Post new topic Reply to topic  [ 8 posts ] 
Author Message
PostPosted: Tue Jan 16, 2024 2:16 pm 
Offline

Joined: Thu Oct 26, 2023 5:39 pm
Posts: 5
So I am writing a 6502 emulator, and now wants to test out some programs. I decided to generate the 6502 assembly from C using https://godbolt.org/. I wrote a simple program declaring two variables and then adding them and storing the result on a third variable. The generated assembly output using cc65 2.19 while makes sense, also includes several instructions which (I think) are not part of the original 6502 assembly, for example instructions like 'pushax', 'ldaxysp', 'tosaddax', 'incsp6', etc. I searched a bit, and I understood that since the memory supported by a 6502 processor is extremely small, the cc65 compiler maintains a software stack and these instructions I talked about are used to manipulate them.

Is my understanding correct? Secondly, is there a way to generate 'pure' 6502 assembly from C code, which I can just assemble to into opcodes and run in my emulator?


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 16, 2024 3:23 pm 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1399
Location: Scotland
I'd suggest you start by writing assembly from scratch and not try C - it's harder to write code, but easier to get the toolchain going.

Just start, one instruction at a time and (say) arrange your emulator to return after e.g. a BRK instruction.

When you think it's good enough you can run the Klauss tests on it for 6502 and 65C02.

https://github.com/Klaus2m5/6502_65C02_functional_tests

Although this site has the same tests, but edited for the ca65 assembler which you are (will be) using:

https://github.com/amb5l/6502_65C02_functional_tests

Hope it goes well.

-Gordon

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 16, 2024 3:27 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
Agreed that C isn't a great place to start. For a beginner, I recommend the easy6502 tutorial. (Don't get hung up on syntax differences between assemblers - that's of no importance. The important thing is the mental model you need to build.

Having said that...

Are you sure you're seeing instructions like that? I suspect you're seeing subroutine calls, to helper routines which cc65 makes use of.

I tried adding some compiler options (-Oi -Os --codesize 999 --static-locals), with this result
https://godbolt.org/z/sds77vzcT

where
Code:
char sum(char a, b) {
    char z;
    z=a+b;
    return z;
}


is transformed to
Code:
.proc   _sum: near

L0002:

        jsr     pushax
        ldy     #$00
        lda     (sp),y
        clc
        ldy     #$02
        adc     (sp),y
        sta     L0002
        ldx     #$00
        lda     L0002
        jmp     incsp3

which still has a couple of helpers, but hopefully clear enough from context as to what they are doing. If in further doubt, consult the cc65 documentation.


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 16, 2024 4:38 pm 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 745
Location: Germany
randomkiwi wrote:
for example instructions like 'pushax', 'ldaxysp', 'tosaddax', 'incsp6', etc. I searched a bit, and I understood that since the memory supported by a 6502 processor is extremely small, the cc65 compiler maintains a software stack and these instructions I talked about are used to manipulate them.

those are not instructions, they are functions which the compiler calls to handle the software stack, there are also some more for stuff like multiplication, division, etc, these are supplied by the runtime library of whatever target you're using.
if you have a custom target without a runtime library then the linker will complain about those functions being missing.

as the other's suggested, maybe getting started with assembly would be a better idea. but if your end goal is getting C running then maybe start directly with ca65 (the assembler included with the cc65 utilities). it's a bit more complicated than other assemblers because it generates object files which need to be linked, but once you understand it and are comfortable with it, going from there to C will be a lot easier (plus you don't have to learn a different assembler syntax when writing assembly functions for cc65).


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 16, 2024 9:22 pm 
Offline
User avatar

Joined: Tue Jul 17, 2018 9:58 am
Posts: 104
Location: Long Island, NY
If you wanted to make it "pure" assembly you can look up the assembly for these routines in the CC65 source.


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 16, 2024 9:46 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
BTW, to test out an emulator, one typical tactic is to try to run a Basic interpreter - any one of the usual ones. Another very useful tactic is to run Klaus Dormann's test suite, which is very thorough and has found errors in the great majority of emulators (since corrected, of course.)

To just get started, I would probably just take examples from anywhere - perhaps the easy6502 tutorial. Or example code from the reference section of this forum: http://6502.org/source/


Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 17, 2024 11:14 am 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1399
Location: Scotland
Since I have the time I'll write bit more...

To generate executable code from cc65 it needs a few more things than just the compiler. It's a more or less complete set of tools that work in the same way as "the big boys" tools do on a desktop or server system - compiler, assembler and linker. Additionally you need a target platform which specifies the code (& data) load and execution addresses.

The target lets you pick the machine you're running it on - there are many pre-defined such as Apple II, BBC Micro, Commodore PET, OSI and so on. These affect the libraries, load addresses and so on.

The godbolt site is as it says; a compiler explorer. It's designed to let you see and compare the output ff various compilers and not to generate actual runnable code.

So the first thing you need to do is get the cc65 suite installed locally.

Then just write a simple assembly program (and I'm assuming that as you've written an emulator you're familiar with 6502 assembler)

You can then assemble with

ca65 -g myfile.s -o myfile.o -l myfile.l

That generates a relocatable object file called myfile.o with a listing file of myfile.l

To turn this into an executable binary file then:

ld65 -t none -S 0x8000 -vm -m myfile.m -o myfile myfile.o

That takes your relocatable object file, converts it into a standalone binary with a starting address of $8000 and a map file of myfile.m.

Here is a small example: Add numbers in locations 0 and 1 and store the result in 2:

myfile.s:
Code:
; Example
loop:
   clc
   lda   $0
   adc   $1
   sta   $2
   jmp   loop


the assemble command and list the output:

Code:
% ca65 -g myfile.s -o myfile.o -l myfile.l
% cat myfile.l
ca65 V2.17 - Git cd72f816
Main file   : myfile.s
Current file: myfile.s

000000r 1               ; Example
000000r 1  18              clc
000001r 1  A5 00           lda   $0
000003r 1  65 01           adc   $1
000005r 1  85 02           sta   $2
000007r 1  4C rr rr        jmp   loop


The 'r' in the address field means it's a relocatable value as is the 'rr' in the JMP instruction.

Linking and displaying the object file:
Code:
% ld65 -t none -S 0x8000 -vm -m myfile.m -o myfile myfile.o
% odx myfile
000000 18 a5 00 65 01 85 02 4c 00 80                    >...e...L..<


Note the % symbol is my prompt and the odx command is a hex dump command I use. Also note the address after the 4C in the dump - 00 80 which the linker has translated into an absolute value based on the start address given on the command line.

There are many, many more ways to drive the cc65 suite but this is about the simplest.

Hope that helps....

Gordon
(Also writing an emulator after many years using real CPUs!)

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 23, 2024 12:38 am 
Offline

Joined: Mon May 01, 2017 7:13 am
Posts: 82
BigEd wrote:
I tried adding some compiler options (-Oi -Os --codesize 999 --static-locals), with this result
https://godbolt.org/z/sds77vzcT

where
Code:
char sum(char a, b) {
    char z;
    z=a+b;
    return z;
}


is transformed to
Code:
.proc   _sum: near

L0002:

        jsr     pushax
        ldy     #$00
        lda     (sp),y
        clc
        ldy     #$02
        adc     (sp),y
        sta     L0002
        ldx     #$00
        lda     L0002
        jmp     incsp3

which still has a couple of helpers [..]


llvm-mos compiles that same C code into maybe 80% fewer executed instructions:

Code:
sum:                                    ; @sum
        stx     __rc2
        clc
        adc     __rc2
        rts


Verify for yourself: https://godbolt.org/z/a1Mn8PoYq

LLVM-MOS does not, in general, generate code that calls inline stack helper functions. There are no "pushax" or "incsp3" functions and the like inlined... it's all just generated assembly. Those called functions are not exactly cheap on cc65.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 30 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: