6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Sep 20, 2024 2:38 pm

All times are UTC




Post new topic Reply to topic  [ 5 posts ] 
Author Message
 Post subject: Vectoring with the SYM-1
PostPosted: Fri Mar 01, 2019 3:44 pm 
Offline
User avatar

Joined: Wed Aug 17, 2005 12:07 am
Posts: 1228
Location: Soddy-Daisy, TN USA
I consider myself a "D" or maybe "C" student when it comes to 6502 assembly. In my effort to become at least a B student, I'm studying vectors and how people have used them.

For example, take this paragraph from Wikipedia regarding the SYM-1:

Quote:
https://en.wikipedia.org/wiki/SYM-1
...
One of the more subtle features of the SYM-1 was the use of a look up table in the low memory of the 6502. This provided a vectoring function in its operating system to redirect subroutine calls to various input and output drivers, including interrupt servicing. Users were able to develop their own interface routines, and substitute new vectors for the original vectors in the startup UV-EPROM. This seamlessly maintained the normal operation of the board's monitor and languages such as Synertek Systems BASIC. One of the later home/education computers that used this concept extensively was the BBC Micro produced by Acorn Computers in the UK. Some of the other computer designers of this era failed to grasp the significance of this elegant use of vectors to the software mapping of new developments in hardware.


I believe I understand this at a high-level. For example, you have a routine or sub-program that sends a character over the serial port. Something like:

Code:
SENDCHAR:
    LDA #'A'
    STA UART
    RTS


Throughout your program, you use that method a bunch. You have many "JSR SENDCHAR"'s all over the place.

So let's say you're a power user or you want to upgrade the system to not only send the character over a UART, but also record it in a file somewhere. You could alter SENDCHAR and add a branch to the "RECORDLOG" method. But it seems a cleaner way would be to have a vector and have everyone use that instead. Something like:

Code:

SEND_VEC = $1000

; OLD WAY
    LDA #<SENDCHAR
    STA SEND_VEC
    LDA #>SENDCHAR
    STA SEND_VEC + 1
...
    JMP (SEND_VEC)    ;     <--- this is what the rest of the program should use


; NEW WAY (adding RECORDLOG to the mix)
    LDA #<NEWSENDCHAR
    STA SEND_VEC
    LDA #>NEWSENDCHAR
    STA SEND_VEC + 1
    JMP (SEND_VEC)    ;     <--- this is what the rest of the program should use...even after change is made

...

NEWSENDCHAR:
    JSR SENDCHAR
    JSR RECORDLOG
    RTS
...

SENDCHAR:
    LDA #'A'
    STA UART
    RTS
...

RECORDLOG:
    <code here>
    RTS



I hope I'm making sense. Am I understanding the concept correctly?

If so, I'm still puzzled on when you would use that and when you would not. I'm a Java developer by trade so we have lots of little tricks too. But there are always times to use them and times to not.

What are some scenarios where vectoring (hope that's the right term) is best used?

PS,

I also get a little confused on properly handling branching (especially with RTS) in multiple jump tables like that. So if I "JMP (SOMEWHERE)", how does "SOMEWHERE" know to get back from the JMP?


Thanks

_________________
Cat; the other white meat.


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 01, 2019 4:11 pm 
Offline

Joined: Tue Sep 03, 2002 12:58 pm
Posts: 324
I've no experience with the SYM-1. But from a quick look at some source code, it seems their vectors contain JMP instructions. So RAM will contain
Code:
invec JMP intchr

The caller can then JSR invec.

The C64 had a different solution. Vectors in RAM are two bytes, containing the address of the routine. There would then be a JMP (vector) instruction somewhere in ROM (usually before the routine itself), and code would call that.


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 01, 2019 4:24 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
You are very close to a complete understanding, cb. The only twist you're missing is how to return properly from a JMP (COUT) instruction, since the base 6502 doesn't have a JSR (COUT) instruction. The answer is to use a springboard JMP, either in RAM or ROM. When a program wishes to send a char, it simply does a JSR COUT, which saves the proper return address on the stack. The real magic happens next, because at address COUT is a JMP (SEND_VEC) instruction that hops through the RAM vector to the output routine. When the output routine is done, its RTS returns all the way back to the original caller. If COUT is in RAM, you don't even have to make it an indirect jump, because you can modify the address inside the absolute JMP instruction, saving a couple of cycles.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Last edited by barrym95838 on Fri Mar 01, 2019 4:30 pm, edited 2 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 01, 2019 4:28 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
And Acorn also used the two byte vector:
Quote:
0200-0201 User vector(USERV)
0202-0203 BRK vector(BRKV)
0204-0205 Interrupt-request vector 1(IRQ1V)
0206-0207 Interrupt-request vector 2(IRQ2V)
(User interrupt routines vectored here)
0208-0209 OSCLI vector(CLIV)
020A-020B OSBYTE vector(BYTEV)
020C-020D OSWORD vector(WORDV)
020E-020F OSWRCH vector(WRCHV)
0210-0211 OSRDCH vector(RDCHV)
0212-0213 OSFILE vector(FILEV)
0214-0215 OSARGS vector(ARGSV)
0216-0217 OSBGET vector(BGETV)
0218-0219 OSBPUT vector(BPUTV)
021A-021B OSGBPB vector(GBPBV)
021C-021D OSFIND vector(FINDV)
021E-021F File system control entry vector(FSCV)
0220-0221 Event interrupt vector(EVNTV)
0222-0223 User's print routine vector(UPTV)
0224-0225 Used by ECONET to take control of computer(NETV)
0226-0227 Unrecognised VDU23 and PLOT commands(VDUV)
0228-0229 For all keyboard access(KEYV)
022A-022B Insert into buffer vector(INSV)
022C-022D Remove from buffer vector(REMV)
022E-022F Count/purge buffer vector(CNPV)


You could think of these as a way to override a method, or to hook into a routine to add value. Your example of adding print logging is a good one. Another would be to add handling of control codes in an output stream. Or one might add instrumentation to see how often, or when, or with what arguments something is called. Or insert a bug fix!

If the new handler calls the old, then the insertion method can be used again by another handler, so they daisychain. If it doesn't, then it replaces whatever the current chain is with its own behaviour.

The VDUV in the Acorn list is a good example: PLOT commands could be extended to draw ellipses, patterned lines, do flood fill, and so on.

The two IRQ vectors in the Acorn scheme is an interesting facility: one hooks in early, so the new code can act ahead of or instead of the OS behaviour, and the other hooks in late, so it doesn't add latency to the OS behaviour.

(However, in Acorn land an application doesn't call the vectors, it calls the ABI entry points in high memory. That's because the application might be running on the I/O processor or on a second processor. The vectors are a mechanism used on the I/O processor where the OS actually resides.)

BTW I'm not sure about your code snippets. The vector would be modified once, at installation of a driver, to be used for all subsequent uses. So the installation updates the vectors, possibly saving the old value for use by the new routine in case it daisy chains. The installation doesn't call the newly vectored code at all.

(I've a feeling I haven't been terribly clear)


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 01, 2019 6:28 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
Another technique is simply a JMP table. In this case, the system loads the RAM on startup, then calls the proper functions via the "hard coded" values in ROM.

Simple example:
Code:
SENDCHAR: JMP SENDCHARIMPL
GETCHAR: JMP GETCHARIMPL
...

In your code, you simply call JSR SENDCHAR, the JMP vectors through to proper routine, and you can change that address whenever you want to something else.
Code:
    LDA #<SENDANDLOGCHAR
    STA SENDCHAR+1
    LDA #>SENDANDLOGCHAR
    STA SENDCHAR+2

On the '265 board, the ROM interrupts all vector to a RAM table filled with 4 byte JML (JMp Long) instructions to break out of the tyranny of bank 0.


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

All times are UTC


Who is online

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