6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Wed Jun 26, 2024 6:37 am

All times are UTC




Post new topic Reply to topic  [ 82 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6  Next
Author Message
PostPosted: Sat May 14, 2022 3:09 am 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
hth313 wrote:
The startup code is provided with the product, both in precompiled form in the library and as source code. This code is short and cruical to get the system initialized properly before you enter the main() function. It is not only doing data copying, it is also clearing variables that are 0 initialized. There may also be multiple areas that needs to be initialized.

well i'm blind, i found the file in the obvious named "src" folder. thank you!

hth313 wrote:
What output format do you use? Many formats (e.g. ELF, intel-hex, S-records) contain a concept of an entry point that is supported. If you are doing Commodore PRG then https://github.com/hth313/Calypsi-6502-Commodore may offer some inspiration, though it is for 6502.

Just Raw Binary, the header is completely custom and programs are loaded via Serial
to be more specific my custom header is 32 bytes large, first 4 bytes are a hardwired magic number (0x65C81600 in this case), next is a 32-bit address that defines where in memory the program should be loaded into, next is the total amount of bytes to load (32-bit value), and the last one is a 32-bit address that specifies where execution should jump to after loading is done, in order to start the program.

hth313 wrote:
The linker error is because __program_start which is the first executable address must reside in bank 00 as the reset vector is 16 bits, but it seems you have placed it in bank 02. This should be section 'code'.

well that won't be possible, programs will only ever get loaded into "HiRAM" ie bank 2 and above.
there is no reason to have it load code into bank 0 since the boot ROM will automatically do a long jump to the header specified address after the whole program has been loaded into memory.
the startup code should never have to deal with any of the vectors at all, since they are hardwired in the boot ROM.

so i'll modify the startup code to just remove the whole reset section, and put the address of __program_start into the header, hopefully that works.
though the assembler syntax will take some getting used to. plus it's like 5 in the morning here and i haven't slept yet, so i'll do all of that after a quick nap.

thanks for helping me getting this running


Top
 Profile  
Reply with quote  
PostPosted: Sat May 14, 2022 10:06 pm 
Offline

Joined: Sun Oct 07, 2018 6:04 pm
Posts: 30
Proxy wrote:

so i'll modify the startup code to just remove the whole reset section, and put the address of __program_start into the header, hopefully that works.
though the assembler syntax will take some getting used to. plus it's like 5 in the morning here and i haven't slept yet, so i'll do all of that after a quick nap.

thanks for helping me getting this running


That should work fine, just remove the reset vector section. You may need to move the __program_root_section label next to __program_start label. It is used to mark the first section to be included by the linker.


Top
 Profile  
Reply with quote  
PostPosted: Tue May 17, 2022 4:02 am 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
I came across a problem where i was unable to use the assembler, no matter what input parameters or what was in the assembly file it would just silently crash with no error given or anything.
the only reason i knew it crashed was because my batch file checks for %errorlevel% being non 0 after calling AS65816.

I wrecked my brain on this trying to understand what is happening, until i treid it with the unmodified cstartup.s, which did work. but then i changed the file extension and it stopped working.
and i can't say that i'm a big fan of the assembler forcing a specific file extension to be used (also the fact that it doesn't even give the user an error message to work with).
For a compiler it makes sense, .c is always a C source file, .h always a header. But those are standardized... Assembly on the other hand comes in many different flavors, that's why i seperate them all with the file extensions. a65 for 65c02, a816 for 65816, a86k for 68k, arv for RISC-V, etc.
both cc65 and WDC's CC don't care what the files are, if you tell them to assemble some file they'll do it without question.

so please, just have the assembler ignore file extensions for convenience. leave that up to the user to handle.

on a different note how do you import a section into an assembly file?
i got this like in the header used to calculate how large the program is:
Code:
.long (.sectionEnd far - .sectionStart farcode) - 17

but the assembler says that it cannot find the section named "far". the command i'm using is:
Code:
AS65816 --code-model large --data-model large -o cstartup.o cstartup.s


Top
 Profile  
Reply with quote  
PostPosted: Tue May 17, 2022 7:37 pm 
Offline

Joined: Sun Oct 07, 2018 6:04 pm
Posts: 30
I know the Clang system (I use its preprocessor for the assembler) has some ideas of file extensions and that they change the meaning of the input. Also, as you say, assembler extensions are all over the place. I will see if I can make it less picky about the extension used for assembly language, as the assembler binary knows what to expect...

To "import" a section, you need to mention it, like ".section sectionName", that makes it known. You do not need to put anything into (below) it.


Top
 Profile  
Reply with quote  
PostPosted: Wed May 18, 2022 3:55 pm 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
ah! so like the ".section stack" and such at the start of the cstartup file?

but now I'm getting this error:
Code:
cstartup.s:35: expression cannot be represented in object file format
        .long (.sectionEnd far - .sectionStart farcode) - 17    ; How many Bytes to load (Total Program Size - 17)
Terminating due to errors

this should just calculate the size of the file and subtract 17 from it.
the scm file has this line:
Code:
(section farcode code farswitch data_init_table cfar chuge far huge zfar zhuge cstack))

i assume segments are placed in the order of how you put them in this list, so "far" will be the last segment in the output file and "farcode" will be the first.
so to get the size of the output file i just toke the end address of the "far" segment and subtract the start of the "farcode" segment from it, but aparently that won't work?
what alternative way is there?

if it helps here is the full header:
Code:
    .section farcode, noreorder
    .pubweak __program_root_section
__program_root_section:
    .byte 0x65, 0xC8, 0x16, 0x00                            ; Magic Number
    .long .sectionStart farcode                             ; Where to load the Program into Memory
    .long (.sectionEnd far - .sectionStart farcode) - 17    ; How many Bytes to load (Total Program Size - 17)
    .long __program_start                                   ; Entry point of the Program

immediately after the header is the __program_start label with the rest of the code.

also once i actually have an object file, do i have to replace the original cstartup.o in the standard library? i assume i can i just remove it from the library and include the object file in the linker manually for convenience.


Top
 Profile  
Reply with quote  
PostPosted: Thu May 19, 2022 9:05 pm 
Offline

Joined: Fri Sep 01, 2017 6:20 pm
Posts: 6
Proxy wrote:
but after a quick round of google, the C standard just doesn't have binary notation because aparently "hex is good enough"


Octal and decimal are "good" as well....


Top
 Profile  
Reply with quote  
PostPosted: Tue May 24, 2022 8:46 pm 
Offline

Joined: Sun Oct 07, 2018 6:04 pm
Posts: 30
Proxy wrote:
ah! so like the ".section stack" and such at the start of the cstartup file?


Yes, that just makes it know to the assembler, it does not put anything into the stack or define its size in any way.

Quote:
but now I'm getting this error:
Code:
cstartup.s:35: expression cannot be represented in object file format
        .long (.sectionEnd far - .sectionStart farcode) - 17    ; How many Bytes to load (Total Program Size - 17)
Terminating due to errors



The error you get is because the expression is too complex for the ELF format and the available relocations to describe it. The crucial thing here is that the relocation/expression refers to two different sections.

What you are trying to accomplish is better solved by having the linker generate an output file in the desired format. Does the format you use have a name, is it officially known? Then I can add it.

If not, there is PGX and PGZ formats used by Foenix that looks very similar. Would it be possible for you to use either of them? PGZ is currently supported by the linker while PGX is not. The difference is essentially that PGX has a single chunk of code while PGZ supports multiple chunks (if you have that) and PGZ works with both 65816 and 68000 while PGX is only for 65816.

In either case, I prefer that this is solved by an output format, as that is what it is about.

Quote:
also once i actually have an object file, do i have to replace the original cstartup.o in the standard library? i assume i can i just remove it from the library and include the object file in the linker manually for convenience.


When you replace the C startup module you can just add it to the command line. You do not need to remove it from the library and I do not know how to do that in an easy way. The linker is able to live with multiple C startup modules in libraries and there is a mechanism to pick a specified one. This is done using the run-time attribute which is called cstartup, in the original one it has the value "normal". You can put any suitable string there to name you variant and then tell the linker to use it on the command line with --rtattr cstartup=whatever.


Top
 Profile  
Reply with quote  
PostPosted: Wed May 25, 2022 2:18 pm 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
hth313 wrote:
The error you get is because the expression is too complex for the ELF format and the available relocations to describe it. The crucial thing here is that the relocation/expression refers to two different sections.

wait so it's a limitation of the object file format? then what is cc65 using that it works there? or is it because cc65's section symbols are just regular old symbols that get defined at the linking stage, instead of special ".sectionxyz" directives?

hth313 wrote:
What you are trying to accomplish is better solved by having the linker generate an output file in the desired format. Does the format you use have a name, is it officially known? Then I can add it.

If not, there is PGX and PGZ formats used by Foenix that looks very similar. Would it be possible for you to use either of them? PGZ is currently supported by the linker while PGX is not. The difference is essentially that PGX has a single chunk of code while PGZ supports multiple chunks (if you have that) and PGZ works with both 65816 and 68000 while PGX is only for 65816.

it is not an official header, i designed by and for myself to be the "simpliest universal executable header possible" since it's not specific to any one system or architecture.
looking at the ones you mentioned, PGX wouldn't be a good choice as it doesn't specify the size of the program, making it impossible to load from Serial as the CPU wouldn't know when to stop reading/waiting for more data.
PGZ seems good though, i'd like it more if the signature was larger so it can be used for CPU/System Identification. but whatever, it'll do.

hth313 wrote:
In either case, I prefer that this is solved by an output format, as that is what it is about.

any specific reason for that? wouldn't that just mean more work for you instead of just having the users do it themselves in assembly (when possible of course)?

hth313 wrote:
When you replace the C startup module you can just add it to the command line. You do not need to remove it from the library and I do not know how to do that in an easy way. The linker is able to live with multiple C startup modules in libraries and there is a mechanism to pick a specified one. This is done using the run-time attribute which is called cstartup, in the original one it has the value "normal". You can put any suitable string there to name you variant and then tell the linker to use it on the command line with --rtattr cstartup=whatever.

oh i see, so the librarian utility is just a bit bare bones at the moment, i assume someday in the future it will get some more features like adding/removing/extracting individual object files, listing all the symbols in the library, etc.
anyways doing that worked fine and i was actually able to generate an output file for once! milestone reached! :lol:

but the victory was short lived because the generated .pgz file is only 7 bytes large despite the linker's generated .lst file saying that the executable section is 494 bytes in size... so where is all of that code? (btw why is the startup code 490 bytes? that seems a little much)
the .pgz file consists of the "Z" signature byte (5A), a 24-bit address that points to some random location in the "cstack" section (E9 41 02 -> 0x0241E9), and lastly a 24-bit word that is just all 0's.
the c program i'm compiling is just just a main function with a "return 0" at the end.
here the command i used for the linker:
Code:
LN65816 --hosted --rtattr cstartup=SBCv1 -l --output-format pgz -o test.pgz Config\SBC_816.scm Config\cstartup.o Temp\test.o clib-lc-ld-double64.a


and the generated lst file:
Code:
####################
#                  #
# Memories summary #
#                  #
####################

Name       Range         Size    Used    Checksum  Largest unallocated
----------------------------------------------------------------------
DirectPage 000100-0001ff 000100    7.8%  none      0000ec
LoRAM      000200-00efff 00ee00   53.8%  none      006e00
IO         00f000-00f0ff 000100    0.0%  none      000100
VRAM       010000-01ffff 010000    0.0%  none      010000
HiRAM      020000-08ffff 070000    3.7%  none      06bd8e


####################
#                  #
# Sections summary #
#                  #
####################

Name            Range          Size    Memory     Fragments
-----------------------------------------------------------
registers       000100-000113  000014  DirectPage 1
stack           000200-0041ff  004000  LoRAM      1
heap            004200-0081ff  004000  LoRAM      1
cstack          020000-023fff  004000  HiRAM      1
zfar            024000-024083  000084  HiRAM      2
farcode         024084-024261  0001de  HiRAM      9
data_init_table 024262-02426b  00000a  HiRAM      1
farcode         02426c-024271  000006  HiRAM      3


##########################
#                        #
# Memory sizes (decimal) #
#                        #
##########################

Executable       (Text):   494 bytes
Data                   :   132 bytes
Non-initialized        : 49172 bytes


interestingly, when i change "--output-format pgz" to "--output-format raw" to see if it's just a problem with the format, the linker straight up crashes with this error:
Code:
internal error: Translator\Linker\Output\Raw.hs:14:1-60: Non-exhaustive patterns in function dumpRawProgram


so am i doing something wrong here and the linker won't tell me, or is it just having a bad day?


Top
 Profile  
Reply with quote  
PostPosted: Wed May 25, 2022 5:34 pm 
Offline

Joined: Sun Oct 07, 2018 6:04 pm
Posts: 30
Proxy wrote:
wait so it's a limitation of the object file format? then what is cc65 using that it works there? or is it because cc65's section symbols are just regular old symbols that get defined at the linking stage, instead of special ".sectionxyz" directives?


Unresolved expressions are turned into relocations. What relocations are available depends on the target. In general, relocations are really simple and meant to resolve a single location with an offset. The result can be stored into the code stream in different ways, typically needed by the target at hand are provided.

The section start, end and size are just a name mangled symbol in Calypsi that is deciphered by the linker. This is of course non-standard, but it was the only way I could see that could do it. I limit my extension to a single section, no arbitrary expressions are allowed in ELF, which is why it failed. It could potentially be done in a different (non-standard) way, but that is how I implemented it.

Quote:
it is not an official header, i designed by and for myself to be the "simpliest universal executable header possible" since it's not specific to any one system or architecture.
looking at the ones you mentioned, PGX wouldn't be a good choice as it doesn't specify the size of the program, making it impossible to load from Serial as the CPU wouldn't know when to stop reading/waiting for more data.
PGZ seems good though, i'd like it more if the signature was larger so it can be used for CPU/System Identification. but whatever, it'll do.

PGZ is defined for 65816 and 68000, well, any byte oriented target that usues either 24 and 32 bit address ranges.

Quote:
hth313 wrote:
In either case, I prefer that this is solved by an output format, as that is what it is about.

any specific reason for that? wouldn't that just mean more work for you instead of just having the users do it themselves in assembly (when possible of course)?


Well, you can see from all the hoops you are trying to do that it takes time. It is that "when possible" and trying to figure out how to do it. The header and metadata also exists in memory when done this way, when it really just is supposed to be in the file.

The PGZ format is about 20 lines of code for me, it is a simple format. Adding another (simple) format is not all that expensive, but there is of course long term maintenance of it. I think the best is to use an existing format, if there are limitations with them, then I think the second best is to add a new format to take care of that use-case. It may also be possible to write a simple converter tool.

Quote:
oh i see, so the librarian utility is just a bit bare bones at the moment, i assume someday in the future it will get some more features like adding/removing/extracting individual object files, listing all the symbols in the library, etc.


The file format used is one of the two that is common on Unix, you should be able to use other (existing) tools to manipulate the library archives.

Quote:
anyways doing that worked fine and i was actually able to generate an output file for once! milestone reached! :lol:

but the victory was short lived because the generated .pgz file is only 7 bytes large despite the linker's generated .lst file saying that the executable section is 494 bytes in size... so where is all of that code? (btw why is the startup code 490 bytes? that seems a little much)

[...]

interestingly, when i change "--output-format pgz" to "--output-format raw" to see if it's just a problem with the format, the linker straight up crashes with this error:
Code:
internal error: Translator\Linker\Output\Raw.hs:14:1-60: Non-exhaustive patterns in function dumpRawProgram


so am i doing something wrong here and the linker won't tell me, or is it just having a bad day?

The internal error is caused by a bug. The Raw format only supports one "progbits" memory and there is a guard that checks if there are multiple. In this case it is none and that error guard did not trigger, but the code later did as it expected one. I need to add another error guard for this case.

The list file looks a bit fishy in that it seems like it has managed to mix zfar and farcode in the same memory, which is not allowed. zfar is bss (no bits) and farcode has bits.

What version are you using of Calypsi 65816? Is your project available on some hosting service so that I can take a look at it?

I would like to see the .scm file.

Rather than discussing here, maybe you could file an issue at https://github.com/hth313/Calypsi-tool-chains


Top
 Profile  
Reply with quote  
PostPosted: Wed May 25, 2022 7:14 pm 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
hth313 wrote:
The list file looks a bit fishy in that it seems like it has managed to mix zfar and farcode in the same memory, which is not allowed. zfar is bss (no bits) and farcode has bits.

I'll open an issue on that Internal bug in a minute, but i just want to quickly jump on this bit here.
i thought we already solved that a page ago or something with the "--hosted" commandline parameter?

EDIT: well just removing all the sections seems to work for that just fine.


Top
 Profile  
Reply with quote  
PostPosted: Tue Oct 04, 2022 3:04 pm 
Offline

Joined: Fri Jul 03, 2020 2:44 pm
Posts: 8
Hi,

I'd like to try this compiler on a home brewed 65C02 from the late 1980s. The memory mapping is straight forward:
RAM - $0000-$7FFF (User program space: $0300-$7FFF)
I/O - $8000-$BFFF
EEPROM - $C000-$DFFF
EPROM (BIOS/Monitor) - $E000-$FFFF

I don't see a generic section in the manual for homebrew/custom boards and trying to figure out exactly what I need to do to get this to work. I would like to run programs in ram(ie. starting at $0300). To download the program to the board, I'd compile for raw output.

My guess for the .scm file would be something like:
Code:
(define memories
  '((memory program
            (address (#x300 . #x6fff))
            (section (programStart #x300) (startup #x300)
                     code switch data cdata data_init_table izpage idata))
    (memory zeroPage
            (address (#x0 . #xf0))
            (section registers zzpage zpage))
    (memory stack
            (address (#x100 . #x1ff))
            (section stack))
    (memory data
            (address (#x7000 . #x7fff))
            (section cstack data zdata heap))
    (block cstack (size #x400))               ; C stack size
    (block stack  (size #x100))               ; machine stack size
    (block heap   (size #x200))               ; heap size
    ))

I really have no idea what my startup.s should look like. The simplest one I found was at:
https://github.com/hth313/Calypsi-6502-Commodore/commit/1f7a1b62008a6321bce1a75131d37864f074d24c
for the C64. I've commented out what I think I don't need? Maybe I need to add some other code?

startup.s
Code:
              ;; Variant, change attribute value if you make your own
              .rtmodel cstartup ;;,"c64"
              ;; External declarations
              .section cstack
              .section data_init_table
              .extern main, exit
              .extern _Zp, _Vsp, _Vfp

              .section programStart, root ; to be at address 0x300
              .public __program_root_section, __program_start

              .section programStart, root ; to be at address 0x300
__program_root_section:
;;for C64?
;;              .word   nextLine
;;              .word   10            ; line number
;;              .byte   0x9e, " 2062", 0 ; SYS 2062
;;nextLine:     .word   0             ; end of program

              .section startup, noreorder
              .public __calypsi_entry, __program_start

__calypsi_entry:
              .section startup, root, noreorder
__program_start:
              lda     #.byte0(.sectionEnd cstack)
              sta     zp:_Vsp
              lda     #.byte1(.sectionEnd cstack)
              sta     zp:_Vsp+1
;;; Initialize data sections if needed.
              .section startup, noreorder
              .public __data_initialization_needed
              .extern __initialize_sections
__data_initialization_needed:
              lda     #.byte0 (.sectionStart data_init_table)
              sta     zp:_Zp
              lda     #.byte1 (.sectionStart data_init_table)
              sta     zp:_Zp+1
              lda     #.byte0 (.sectionEnd data_init_table)
              sta     zp:_Zp+2
              lda     #.byte1 (.sectionEnd data_init_table)
              sta     zp:_Zp+3
              jsr     __initialize_sections
              .section startup, noreorder
              .public __call_initialize_global_streams
              .extern __initialize_global_streams
__call_initialize_global_streams:
              jsr     __initialize_global_streams
              .section startup, root, noreorder
              tsx
              stx     _InitialStack ; for exit()
              lda     #0            ; argc = 0
              sta     zp:_Zp
              sta     zp:_Zp+1
              jsr     main
              jmp     exit
;;; ***************************************************************************
;;;
;;; Keep track of the initial stack pointer so that it can be restores to make
;;; a return back on exit().
;;;
;;; ***************************************************************************
              .section zdata, bss
              .public _InitialStack
_InitialStack:
              .space  1

I don't know how to handle the exit. If I decide to exit my C program, I'd want to jump to the monitor prompt, which is at $E003. Would I use the stub? ie:
Code:
// exit program
void _Stub_exit(int exitCode)
{
   myExit();
}

...then put in some .s file (or part of startup.s?):
Code:
directive:
.section code
.public myExit ; export myExit
myExit: jmp 0xE003

The next item is interrupts. When my board boots, IRQ location $FFFE in EPROM points to $00FD and puts an RTI instruction there. So, when writing in assembler, I'd put a JMP at $00FD, and the IRQ method address in $00FE/$00FF. So, if writing a C program with interrupts, would I initialize with a JMP at $00FD(in startup.s?), then in my C program:
Code:
__attribute__((interrupt(0x00fe)))
MyIRQ()
{
...
}

...and lastly I'd need to figure out the command line options for the compiler and linker. Whew!

Any help appreciated, thanks!


Top
 Profile  
Reply with quote  
PostPosted: Tue Oct 04, 2022 6:31 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8230
Location: Midwestern USA
arrow201 wrote:
I don't see a generic section in the manual for homebrew/custom boards and trying to figure out exactly what I need to do to get this to work. I would like to run programs in ram(ie. starting at $0300)...Any help appreciated, thanks!

The OP for this topic hasn’t been on for over four months. He may not be monitoring this anymore.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Tue Oct 04, 2022 7:58 pm 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
the tool chain github is where the dev is mostly spending their time to answer questions and resolve issues, so i'd recommend you ask over there: https://github.com/hth313/Calypsi-tool-chains

but i can answer some stuff.
arrow201 wrote:
I don't see a generic section in the manual for homebrew/custom boards and trying to figure out exactly what I need to do to get this to work.

when you download the utility it should come with an "example" folder that has a minimal linker setup, and in the "src\lib\lowlevel" folder there is the default cstartup.s that you can either modify directly or copy and then modify for whatever system you have.
though i agree that it should be made much easier to set up a custom environment. so a template example with better commented startup code would be very useful. (you can also ask for that on the github)

arrow201 wrote:
I don't know how to handle the exit. If I decide to exit my C program, I'd want to jump to the monitor prompt, which is at $E003.

in C, exiting from the main function is handled like with any other function, meaning it just returns control to whatever code called it. in the startup code you can see the "JSR main" that calls the main function, and once it finishes/returns it continues with the instructions following it. in the case of your startup code it will execute a "JMP" to the label named "exit"
so it you want it to jump to your monitor you could just define "exit" to equal $E003, or replace the "JMP exit" with "JMP $E003".

arrow201 wrote:
The next item is interrupts. When my board boots, IRQ location $FFFE in EPROM points to $00FD and puts an RTI instruction there. So, when writing in assembler, I'd put a JMP at $00FD, and the IRQ method address in $00FE/$00FF

I wouldn't recommend jumping execution to Zeropage. use an indirect jump instead, which gets it's target address (the address that it jumps to) from memory where the operand address (the one you write in the assembler) points to.
so for example "JMP ($00FE)" reads 2 bytes from address $00FE and $00FF and let's say that the value at the first address was $99 and at the second address $44, meaning that execution will jump to address $4499 and continue there.
this leaves your Zeropage nice and tidy without having to store any code there.

as for Interrupt code in C... it's somewhat complicated. your startup code has to get the address of the function (assembly or C) you want to call and write it to where your IRQ ROM code jumps to. the problem with doing it in C is that any temporary varibles the C environment uses (which are pretty much just in the Zeropage) have to be copied to somewhere else before you can actually call your C interrupt function, which adds a lot of latency to the IRQ function.
maybe also ask the dev about a simple example for interrupt handling since i've honestly never bothered with it myself and all my Interrupt code has been in pure assembly to avoid having to save any C memory values.

on a final note, i recommend including this header file i call "MemAccess.h" that i made that adds some macros for accessing memory and setting/clearing/checking individual bits of varibles/memory locations. which makes accessing memory a lot more readable IMO.
Code:
#ifndef MEMACCESS
   #define MEMACCESS
   
   #include <stdint.h>
   
   #define mread8(addr)         *((uint8_t*)addr)
   #define mread16(addr)         *((uint16_t*)addr)
   #define mread32(addr)         *((uint32_t*)addr)
   
   #define mwrite8(addr, val)      *((uint8_t*)addr) = val
   #define mwrite16(addr, val)      *((uint16_t*)addr) = val
   #define mwrite32(addr, val)      *((uint32_t*)addr) = val
   
   #define bitread(var, bit)      (((var) >> (bit)) & 1U)
   #define bitset(var, bit)      ((var) |= (1U << (bit)))
   #define bitclr(var, bit)      ((var) &= ~(1U << (bit)))
   
   #define bitwrite(var, bit, val)   ((val) ? bitset(var, bit) : bitclr(var, bit))
#endif


I also recommend learning the "stdint" sytax for variables. so "uint16_t" instead of "unsigned int", "int8_t" instead of "char", etc. on a CPU like a 65C02 keeping track of your vairable's widths can save you a fair amount of execution time and memory.

Either way, I hope this was atleast somewhat helpful.


Top
 Profile  
Reply with quote  
PostPosted: Tue Oct 04, 2022 11:17 pm 
Offline

Joined: Fri Jul 03, 2020 2:44 pm
Posts: 8
Proxy, thank you very much for your reply! I didn't know there were minimal examples in the install. I'll d/l and install the utility and see how far I get. :) Sorry, I didn't explain the IRQ setting well. I meant to say how I'd initialize the IRQ vector. It would be called from an ASM program as you said, JMP ($00FE). Thank you for the MemAccess idea and example. Yes, it makes code much more readable. Also, thanks for the "int8_t", etc. idea as well. I use this syntax in microcontroller code and makes sense to continue with it here to keep things clear.

Thanks again for all the information!


Top
 Profile  
Reply with quote  
PostPosted: Mon Oct 24, 2022 3:12 pm 
Offline

Joined: Fri Jul 03, 2020 2:44 pm
Posts: 8
Following up to my previous post, I have a home brewed 65C02 from the late 1980s. Memory mapping:
RAM - $0000-$7FFF (User program space: $0300-$7FFF)
I/O - $8000-$BFFF
EEPROM - $C000-$DFFF
EPROM (BIOS/Monitor) - $E000-$FFFF

The purpose is to run small experimental programs in RAM. Another objective is to have the code execute at the first loaded address, ie. if I load(raw format) between $1000-10FF, I want to do a run at $1000. If not configured right, the start of execution could be anywhere between the start/end addresses. I don't know if I did this the best way, but I have it working the way I want(so far, blinking a LED from a VIA(6522)) doing the following:

linker.scm
Code:
(define memories
  '((memory program
            (address (#x1000 . #x6fff))
            (section (startup #x1000)
                     code switch data cdata data_init_table izpage idata reset))
    (memory zeroPage
            (address (#x0 . #xbf))      ; $C0-$FF - used by monitor, IRQ, NMI
            (section registers zzpage zpage))
    (memory stack
            (address (#x100 . #x1ff))
            (section stack))
    (memory data
            (address (#x7000 . #x7fff))
            (section cstack data zdata heap))
    (block cstack (size #x400))               ; C stack size
    (block stack  (size #x100))               ; machine stack size (page 1)
    (block heap   (size #x200))               ; heap size
    ))

cstartup.s
Code:
; cstartup.s
;
   ; External declarations
   .section stack          
   .section cstack
   .section data_init_table

   .extern main
   .extern _Zp, _Vsp, _Vfp
       
   .section startup, root, noreorder
   ldx     #.byte0(.sectionEnd stack)
   txs
   lda     #.byte0(.sectionEnd cstack)
   sta     zp:_Vsp
   lda     #.byte1(.sectionEnd cstack)
   sta     zp:_Vsp+1

   lda     #0         ; argc = 0
   sta     zp:_Zp
   sta     zp:_Zp+1
   jsr     main
   jmp     0xE000      ; MBII monitor

Next is interrupts. I don't know, but it seems setting up the IRQ will not work the way I want it to using the attribute commands or modifying the interrupt.h(which appears to have been made for the C64?) In summary, I want my IRQ routine to be in RAM, in the same program space. How this board works is the BIOS IRQ points to page 0(EPROM - $FFFE=$FD, $FFFF=$00). In page 0, you would then do a JMP to your IRQ in program space.
ie:
Program lives at $1000-$10FF and lets say the IRQ routine compiles to be at $1034
When an IRQ comes in:
- $FFFE/$FFFF --> jumps to $00FD
- then the 3 p.0 locations would have to be: $00FD=$4C(JMP), $00FE=$34, $00FF=$10 --> JMP $1034
The way I got this to work is:

main.h
Code:
struct IRQ
{
   uint8_t      Jmp;
   uint16_t   *Addr;
};
#define IRQ_VECTOR (*(volatile struct IRQ*) 0x00FD)

main.c
Code:
int main ()
{
   __disable_interrupts();

   InitIRQ();
...   
   __disable_interrupts();
   return (0);
}

void InitIRQ()
{
   IRQ_VECTOR.Jmp = 0x4C;   // JMP
   IRQ_VECTOR.Addr = (uint16_t   *)&MyIRQ;
   
   __enable_interrupts();   
}

void MyIRQ()
{
   MBII_StartINT();   // asm routine to push regs
...
   MBII_EndINT();      // asm routine to restore regs
   
//   __asm("rti");   // compiler doesn't produce code!
}

I noticed the "__asm" command doesn't do anything. There's no compile/link error, but it also doesn't produce any code when looking at the .lst file. I've put a "RTI" in my MBII_EndINT(); but that doesn't satisfy the final JSR and my guess is it will eventually crash(?)


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 82 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 41 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: