6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 10, 2024 8:43 pm

All times are UTC




Post new topic Reply to topic  [ 10 posts ] 
Author Message
 Post subject: Symbols and linking
PostPosted: Sat Oct 12, 2013 7:00 pm 
Offline

Joined: Mon Aug 05, 2013 10:43 pm
Posts: 258
Location: Southampton, UK
This is a fairly imprecise question. Sorry if the answer is obvious but for the life of me I can't figure it out.

For my 6809 computer (this is not a CPU-type specific question, I hope) I have implemented a monitor and a set of helper routines for things like serial IO, SPI, and (in progress) IDE interface routines. Everything is working well.

Now I would like to write "user" programs, and write them into a file on the CompactFlash which will be read by the monitor into RAM and executed. The problem I'm having is that my user program needs to resolve label symbols, so I can do jumps into the ROM. Obviously I could use literal addresses, but I was wondering what the proper solution to this problem is? I'm guessing there is some way to generate a symbol list and then use that in my user code, but I can't figure out how to do this?

I'm using a09, but I'm sure this is a general problem which would effect anyone doing the same thing with a 6502...

Lawrence

_________________
8 bit fun and games: https://www.aslak.net/


Top
 Profile  
Reply with quote  
 Post subject: Re: Symbols and linking
PostPosted: Sat Oct 12, 2013 7:46 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8539
Location: Southern California
So you're assembling for RAM without re-assembling the ROM contents? How stable are they? The first thing that comes to mind is to have a list of equates at the top of your source code file (or in an include file if it's for multiple RAM applications), with those entry points, so you can still call them by name, not number, in your code; but then if you make a change in the ROM, obviously you'll have to change the equates. If the list isn't too long (up to 128 or 256 entry points), what some kernels have done is to put the entry points of interest into a jump table, so even if the entry point's address changes in later versions, the same index into the table will reach it without re-assembling the application. It's less efficient, but may be necessary sometimes. Maybe someone else will remember a better way it has been done.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject: Re: Symbols and linking
PostPosted: Sat Oct 12, 2013 8:13 pm 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
Depending on the tools, you may be able to export a symbol table from the ROM, and import it in the user project. If the tools don't have that option, you may be to able to run some scripts to perform that task.


Top
 Profile  
Reply with quote  
 Post subject: Re: Symbols and linking
PostPosted: Sat Oct 12, 2013 8:52 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8481
Location: Midwestern USA
A common practice is to have a fixed jump table located at one end of the firmware so user applications can call kernel functions without having to know exactly where they are in the kernel. Looking at the Commodore 64's kernel ROM would give you an excellent example of how this would be done. I used the same method in my POC V1.1's BIOS:

Code:
;================================================================================
;
;BIOS ROM JUMP TABLE
;
;   ————————————————————————————————————————————————————————————————————
;   Applications requiring BIOS services should utilize this jump table,
;   as the internal locations of BIOS functions are subject to change in
;   future revisions.  All functions are subroutines.
;
;   WARNING: Do not edit this table!
;   ————————————————————————————————————————————————————————————————————
;
alarm    =$00e000              ;set an alarm
basina   =$00e003              ;read from TIA-232 channel A
basinb   =$00e006              ;read from TIA-232 channel B
bsouta   =$00e009              ;write to TIA-232 channel A
bsoutb   =$00e00c              ;write to TIA-232 channel B
constime =$00e00f              ;set console clock (deprecated)
getdtr   =$00e012              ;read date & time from RTC
getnvr   =$00e015              ;read data from NVRAM
getutim  =$00e018              ;read system up time
hrst     =$00e01b              ;restart system (soft reset)
mon      =$00e01e              ;enter machine language monitor
plot     =$00e021              ;position console cursor
putdtr   =$00e024              ;write date & time to RTC
putnvr   =$00e027              ;write data to NVRAM
scsicmd  =$00e02a              ;execute SCSI command
scsiinit =$00e02d              ;initialize SCSI subsystem
scsiparm =$00e030              ;get SCSI device parameters
sprint   =$00e033              ;print string to console
utdelay  =$00e036              ;generate user-defined time delay
;

In this case, the jump table is at the start of ROM address space ($00E000). Commodore placed their jump table near the end of ROM address space. Either way, the principle is the same: the user program calls the desired function by JSR <function>, e.g., JSR BSOUTA.

I'm not very familiar with the 6809 assembly language, but seem to recall that it includes SWI (SoftWare Interrupt). You could implement a methodology in which the user application loads the 6809's registers as required by the kernel call and then SWI. A dispatcher that would be invoked when SWI is processed by the 6809 would use an index to determine which function is to be run. This would have the advantage of working no matter where in core the kernel is located. It's principle disadvantage would be in slower performance and a bit more complexity in the kernel. I'd give you an example of the required code, except the only thing I have is in 65C816 assembly language.

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


Top
 Profile  
Reply with quote  
 Post subject: Re: Symbols and linking
PostPosted: Sun Oct 13, 2013 4:38 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 410
Location: Minnesota
Quote:
The problem I'm having is that my user program needs to resolve label symbols, so I can do jumps into the ROM. Obviously I could use literal addresses[...]


It's critical to know the literal addresses if you want to use them in another, otherwise unrelated program - even if you use a jump table (which is a Good Thing) you still have to know the locations of the table entries themselves. So that's a plus. But how do you know what they are? Learning how you came by that knowledge might suggest a useful next step.


Top
 Profile  
Reply with quote  
 Post subject: Re: Symbols and linking
PostPosted: Sun Oct 13, 2013 8:37 am 
Offline

Joined: Mon Aug 05, 2013 10:43 pm
Posts: 258
Location: Southampton, UK
Thanks for the tips! The use of a jump table sounds like the way forward. I don't know which assembler is popular here, but I have switched my code to asxxxx (as6809 for me but it has every 8but CPU I know) which seems much more sophisticated then the a09 I was using before, though the directive syntax is a little odd (.org vs org etc). In the link stage, aslink can write out a .map file. Then a perl script I wrote turns the map into a "include" file of sorts, turning exported labels into equates. The last step would be to add the jump table into the ROM and export those symbols instead of the real subroutine addresses. Then my little external programs will work across ROM changes. Excellent!

Sans the jump table, I got my first "hello world" program working last night. :)

While the monitor itself is entered by a software interrupt, I think using interrupts for entering ROM routines would be a bit of a performance hit, and added complexity. This is how Motorola implemented there "reference" monitor though according to the docs... So it's obviously the proper way to do things, I just don't see the need?

So for now I will stick with a exported jump table. Fast enough and easy to maintain.

Lawrence

_________________
8 bit fun and games: https://www.aslak.net/


Top
 Profile  
Reply with quote  
 Post subject: Re: Symbols and linking
PostPosted: Sun Oct 13, 2013 8:55 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8539
Location: Southern California
BRK with the signature byte following is probably the most compact way, but puts an extra burden on the ISR and dramatically cuts the performance when you have real interrupts. I don't like BRK myself, and have never used it except in school in 1982.

LDX #<routine number>, JSR to the routine that does the jump table would be five bytes which is not terrible, but there's still kind of a lot of cycles of overhead.

Another possibility is that instead of the list of addresses composing the table that you index into, you JSR (with no indexing) into a list of three-byte JMPs to the actual routine addresses which can change as often as you like. This list of JSRs can be as long as you want. So for example if you have a routine to output a character to the current display or print device and it's called "OUTCH" for "output character" and its JMP line is permanently fixed to address $E211, you might do JSR OUTCH (ie, JSR $E211) in your program in RAM, and that takes you to the line in ROM where it says JMP ____ to wherever the real routine is regardless of how many times you've revised the ROM.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject: Re: Symbols and linking
PostPosted: Sun Oct 13, 2013 8:06 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8481
Location: Midwestern USA
GARTHWILSON wrote:
BRK with the signature byte following is probably the most compact way, but puts an extra burden on the ISR and dramatically cuts the performance when you have real interrupts. I don't like BRK myself, and have never used it except in school in 1982.

LDX #<routine number>, JSR to the routine that does the jump table would be five bytes which is not terrible, but there's still kind of a lot of cycles of overhead.

My 816NIX operating system that will soon be operating :lol: will use COP for calling user-accessible APIs. The basic process will be for the calling function to push a stack frame with whatever parameters are needed by the API, load .C (C-accumulator) with the non-zero API number and then execute COP $00. The COP ISR front end will push the registers, check the API number for range and if in range, ASL it to generate a 16 bit index and give the index to .X (X-register). A JMP (<apitab>,X) instruction will route execution to the appropriate API handler. As individual APIs have varying numbers of parameters, each one will be responsible for processing the caller's stack frame.

When an API has finished it will jump back to one of two common exit points: one to handle a successful completion and the other to handle any error conditions. The successful completion handler will write any return register values to the appropriate stack locations and clear the carry bit in the stack copy of the status register (SR). The error condition handler will write an error number into the accumulator stack entry and set the carry bit in the stack copy of SR. The final steps before RTIing back to the caller will be to realign the stack to discard the parameters pushed by the caller and reload the registers from the stack. It sounds somewhat convoluted. However 65C816 makes it pretty easy to implement this scheme.

My reasons for going with a software interrupt API call instead of a jump table are several:

  1. Portability. The software interrupt method allows the kernel to be anywhere in RAM, as user mode processes don't have to know where to look for a jump table. I can load the kernel into any of the 256 possible RAM banks in an '816 system and not have to inform user mode processes about it. All that is necessary is to point the COP vector at $00FFE4-$00FFE5 to the COP ISR front end, which can then JML (JuMp Long) to wherever the kernel is located.

  2. API stability. If no API jump table is required then one doesn't have to be updated if a new kernel API is added. If a jump table were used, every program that makes a kernel call (which would be just about every program) would have to be reassembled, recompiled or relinked to link in the new jump table. On the other hand, using the software interrupt method means that only the next unused API number has to be added to the API number table. Programs that were written prior to the change don't have to know about the new API table unless they are going to be modified to use the new API.

  3. Context change. POC V2's hardware will have some form of memory protection and (eventually) a user/supervisor mode context feature. The hardware will be able to tell when a kernel call has been made because a software interrupt causes the '816's VPB (vector pull) output to negate during cycles 7 and 8 of interrupt acknowledgement. This will cause the switch to supervisor mode, allowing code to touch hardware registers, etc., things which user mode processes won't be allowed to do. I wouldn't be able to readily implement this feature if API calls were via a jump table.

I decided to use COP instead of BRK for API calls so BRK could remain a means of interrupting program execution and starting an M/L monitor. BRK on the '816 running in native mode doesn't use the IRQ vector, so the overhead of testing for the B-bit in the SR copy wouldn't be present, even if I did use BRK instead of COP. As Garth noted, having to test for the B-bit in a 65C02 ISR does slow down interrupt processing a bit and of course, clobbers both .A and .X.

Quote:
Another possibility is that instead of the list of addresses composing the table that you index into, you JSR (with no indexing) into a list of three-byte JMPs to the actual routine addresses which can change as often as you like. This list of JSRs can be as long as you want. So for example if you have a routine to output a character to the current display or print device and it's called "OUTCH" for "output character" and its JMP line is permanently fixed to address $E211, you might do JSR OUTCH (ie, JSR $E211) in your program in RAM, and that takes you to the line in ROM where it says JMP ____ to wherever the real routine is regardless of how many times you've revised the ROM.

That's the classic kernel jump table that was implemented in all 8-bit Commodore hardware.

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


Top
 Profile  
Reply with quote  
 Post subject: Re: Symbols and linking
PostPosted: Sun Oct 13, 2013 10:54 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8539
Location: Southern California
Quote:
If a jump table were used, every program that makes a kernel call (which would be just about every program) would have to be reassembled, recompiled or relinked to link in the new jump table.

It's not necessary. You put the routine number in X (always an even number), then do JSR Kernel_Call (or whatever you want to call it). Then the kernel has, at an address that does not change with updates:
Code:
Kernel_Call:
        JMP  (kc1,X)

 kc1:   <addr of 1st routine>
        <addr of 2nd routine>
        <addr of 3rd routine>
        <etc.>

Updates to the kernel might change the contents of the table, but not the address of Kernel_Call, nor the X value you called Kernel_Call with. This method allows up to 128 addresses in the table. Making it allow 256 takes a few more instructions.

For the NMOS processor that lacked the JMP(addr,X) instruction, you can do:
Code:
Kernel_Call:
        LDA  kc1+1, X
        PHA
        LDA  kc1, X     ; Be sure to make the table reflect start addresses
        PHA             ; minus 1, since RTS increments the address by 1.
        RTS             ; Here we use RTS not to return but to synthesize the lacking indexed indirect jump.

 kc1:   <addr of 1st routine>
        <addr of 2nd routine>
        <addr of 3rd routine>
        <etc.>

Edit: Fixed. (I didn't do it right the first time.)

The '816 makes it easiest and quickest with the JSR(addr,X) instruction, eliminating the need for the part before kc1.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
 Post subject: Re: Symbols and linking
PostPosted: Mon Oct 14, 2013 12:31 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8481
Location: Midwestern USA
GARTHWILSON wrote:
The '816 makes it easiest and quickest with the JSR(addr,X) instruction, eliminating the need for the part before kc1.

You'd have to have code prior to the JSR (<addr>,X) that would verify that .X is in range and is an even value. There's no telling what might happen if these checks weren't in place. It would be more efficient to pass the API number in .C (C-accumulator), check it for range, ASL it to generate a table offset and then give it to .X to index the jump table. So the call would be LDA #APINUM JSR APIENT and the internal code would be JMP (<addr>,X).

That said, I'm going the software interrupt route for reasons of hardware control, something that the '816's stack capabilities make easy to do. If I were to calling APIs with JSR, I'd have to allow user mode processes to access kernel space, which would violate a basic principle of memory protection. Using a software interrupt instead means I can maintain the so-called "Chinese Wall" that separates user applications from the operating system and in the process, have a means to automatically change the hardware from user mode to supervisor mode.

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


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

All times are UTC


Who is online

Users browsing this forum: barnacle and 0 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: