6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Thu Jun 20, 2024 3:00 pm

All times are UTC




Post new topic Reply to topic  [ 564 posts ]  Go to page Previous  1 ... 29, 30, 31, 32, 33, 34, 35 ... 38  Next
Author Message
PostPosted: Thu Jul 01, 2021 8:33 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8222
Location: Midwestern USA
Dr Jefyll wrote:
And I like the clock-stretch circuit you've got there, BDD. I've taken the liberty of editing away some detail to clarify its workings.

Of course, I stole your basic J-K flop wait-state design. :P

Quote:
Well, there are two possible solutions to investigate. If the glue logic that generates /WSE can't be made faster, maybe the /WSE timing can be accommodated as-is...

I've thought about what you have suggested. Scabbing and bodging V1.3 is greatly complicated for me by the use of SOIC packages. I really can't see well enough to work with that stuff—I had to get someone to solder the SOIC stuff for me. So I'm unlikely to do any hardware hacking on the unit. When I designed V1.3, I concluded that 16 MHz would likely be the ceiling if I were lucky. I'm happy with it, as it is stable and plenty fast.

Competing for time is my locomotive, which is in the process of receiving its final electrical system, and is also awaiting final body prep and livery. Progress in that area has been slow for a variety of reasons, not the least of which are my ongoing cardiac calamities.

I've got something else cooking 65816-wise on which I rather devote my hobby computing time. This new design builds on the knowledge gleaned from V1.2 and V1.3. In the process, the chip count will be greatly reduced, resulting in more timing headroom and, I hope, a return to the 20 MHz that was achieved in V1.2.

Incidentally, to anyone who is reading this but plans to build a 65C02 unit, the clock-stretching circuit that Jeff devised with the 163 synchronous counter would be ideal for wait-stating that slow ROM or I/O device.

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


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 02, 2021 3:57 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3366
Location: Ontario, Canada
BigDumbDinosaur wrote:
the clock-stretching circuit that Jeff devised with the 163 synchronous counter would be ideal for wait-stating that slow ROM or I/O device.
Ha! :) "Ideal" is too strong a word. Almost any decision involves upsides and downsides. So, the trick is to make a choice that best suits the circumstances. :!:

For anyone just tuning in, my thread about the '163 circuit (and about the little JK nugget) is here: RDY vs CLOCK STRETCHING. Includes 2 very simple circuits.

Re clock-stretching, ROM is easier to deal with than devices (such as I/O) which can be written to. A ROM is easy because it doesn't care if you let Phi2 go high rather early. But to deal with a clock-stretched write you'll probably need to keep Phi2 low at first so the device has lots of address setup time before the write (which is timed by Phi2) commences. If you don't allow enough address setup time then you'll end up with writes to addresses other than the one you intended. :(

In order to "keep Phi2 low at first," clearly you need /WSE (the "stretch the clock" signal) to become valid before the time Phi2 will rise if it's going to. (It would not be OK to let Phi2 rise but then have /WSE reverse the decision and bring Phi2 low again a few ns later.) And getting /WSE to become valid sufficiently early can be a challenge if your scenario involves high clock speeds.

Since he's attempting high clock speeds but is dealing with ROM only, BDD's circuit may be a better choice for his scenario than my '163 arrangement. That's because it allows the mod I suggested for accommodating a /WSE signal that arrives late. (And the fact that his circuit doesn't keep Phi2 low at first is not a problem.)

In my post about the '163 circuit I should probably be more clear about its ability to keep Phi2 low at first. It will or won't do so, depending on the choices one makes. I may edit the post in future. Presently, the diagram illustrates a reload value of 1101, and the output is taken from Q3. This happens to be a combination that does NOT keep Phi2 low at first. One which would is a reload value of 1001, with the output taken from Q2.

-- Jeff

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 02, 2021 8:42 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8222
Location: Midwestern USA
Dr Jefyll wrote:
BigDumbDinosaur wrote:
the clock-stretching circuit that Jeff devised with the 163 synchronous counter would be ideal for wait-stating that slow ROM or I/O device.
Ha! :) "Ideal" is too strong a word. Almost any decision involves upsides and downsides. So, the trick is to make a choice that best suits the circumstances. :!:

I can tell you never took the Dale Carnegie salesmanship course. :D In selling something, a degree of hyperbole is considered a necessary part of the sales pitch. Just ask any new car salesman. :lol:

Quote:
Since he's attempting high clock speeds but is dealing with ROM only, BDD's circuit may be a better choice for his scenario than my '163 arrangement.

I/O is involved as well. The DUARTs are good up to about 15 MHz. So I've got the circuit rigged up to wait-state any access to I/O or ROM.

Quote:
In my post about the '163 circuit I should probably be more clear about its ability to keep Phi2 low at first. It will or won't do so, depending on the choices one makes. I may edit the post in future. Presently, the diagram illustrates a reload value of 1101, and the output is taken from Q3. This happens to be a combination that does NOT keep Phi2 low at first. One which would is a reload value of 1001, with the output taken from Q2.

Where I think the ability to stretch both phases of the clock would be of value would be in interfacing older 65xx peripherals to a modern 65C02 or 65C816 running at high clock speed. As 65xx peripherals are dependent on Ø2 to operate, and since setup for register access occurs during Ø2 low, a "symmetric" stretch probably makes sense. My circuit only stretches the high phase, which is acceptable given neither ROM or I/O is clocked by Ø2.

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


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 03, 2021 2:19 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3366
Location: Ontario, Canada
BigDumbDinosaur wrote:
My circuit only stretches the high phase, which is acceptable given neither ROM or I/O is clocked by Ø2.
While your I/O isn't exactly clocked by Ø2, I see Ø2 is used to generate I/O's /RD and /WR signals, so it amounts to the same thing, and the same concern arises. Because the circuit doesn't stretch the low phase, a write may commence before the I/O device has had sufficient address setup time. So, that's something to keep in mind. (I'm not saying you have a confirmed problem due to this.)

-- Jeff

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 03, 2021 4:03 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8222
Location: Midwestern USA
Dr Jefyll wrote:
While your I/O isn't exactly clocked by Ø2, I see Ø2 is used to generate I/O's /RD and /WR signals, so it amounts to the same thing, and the same concern arises. Because the circuit doesn't stretch the low phase, a write may commence before the I/O device has had sufficient address setup time. So, that's something to keep in mind. (I'm not saying you have a confirmed problem due to this.)

None of the devices I use demands that /CS be asserted before /RD or /WD as a condition of reliable operation. In the case of the SRAM, performance is better if /CS is asserted before /RD or /WD, but even if /CS is asserted at the same time as or after the other two, the 12ns rating is still guaranteed.

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


Top
 Profile  
Reply with quote  
PostPosted: Mon Jul 05, 2021 1:16 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3366
Location: Ontario, Canada
BigDumbDinosaur wrote:
My circuit only stretches the high phase, which is acceptable given neither ROM or I/O is clocked by Ø2.
Hmm, let's back up a little.

BigDumbDinosaur wrote:
Where I think the ability to stretch both phases of the clock would be of value would be in interfacing older 65xx peripherals to a modern 65C02 or 65C816 running at high clock speed.
Yes. But even a non-65xx peripheral such as your 28L92 DUART will require stretching of the Ø2 low phase if the CPU clock rate is raised sufficiently.

I'm not saying you've reached that point. But one can't conclude that omitting the low-phase stretch is acceptable simply because "neither ROM or I/O is clocked by Ø2." Your DUART is driven by signals derived from Ø2. (diagram below)

The #1 concern is to avoid writing to an incorrect address (although indeed some devices have registers which are also sensitive to spurious reads). To avoid accessing an incorrect location, we need to wait until the address from the CPU is stable, then continue waiting (with Ø2 low) until the device's address setup time has been satisfied. Only then is it alright for Ø2 to go high, allowing the access to commence. This is true both for 65xx and non-65xx peripherals (and for RAM, too, although RAM is presumably not an issue for POC at present).

I haven't studied POC in detail, and at first I thought the clock stretch only applied to ROM. With ROM it's OK if the stretcher circuit only stretches the high phase. The lack of an extra allocation of low phase (ie: address setup time) might mean the read initially occurs from an incorrect address, but a ROM can recover from that. As long as the total cycle time is sufficient, things will eventually sort themselves out, with no harm done.

Writes are a different story. While it might be tolerable to accidentally read an incorrect address before reading the correct address, it is definitely NOT okay to accidentally write to an incorrect address before writing to the correct address! Now that you've reminded me you're also stretching I/O accesses (which could include writes), it seemed worthwhile to clarify that the Ø2-low phase will in some circumstances require stretching.

-- Jeff


Attachments:
File comment: Even with non-65xx peripherals, it is the usually rise of Ø2 which begins the access
Phi2 usage.png
Phi2 usage.png [ 5.6 KiB | Viewed 122473 times ]

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 30, 2021 11:50 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8222
Location: Midwestern USA
Just to report POC V1.3 has been run smoothly at 16 MHz for a month now. I haven't done too much with it, other than run final tests on my updated 65C816 string manipulation library. I've been running the code at $002000, and the test data is at $010000. So far, so good.

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


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 18, 2021 6:14 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8222
Location: Midwestern USA
Up to the present, all versions of the POC’s basic input/output system (BIOS) have used the typical 6502 method of treating each API function as a subroutine to be called via JSR - RTS.  The 65C816’s architecture limits the scope of JSR - RTS to the bank in which the subroutine is located.  Prior to POC V1.3, only bank $00 existed, so the scope issue was a non-issue.

Starting with V1.3, it is possible to execute code outside of bank $00.  However, such code can’t call BIOS API functions, due to the above-described limitation of JSR - RTS—the BIOS continues to be in bank $00.  Ergo I need to change how API calls are made.  There are two possible methods:

  1. Subroutine call with JSL - RTLJSL is similar to JSR except the former takes a 24-bit address as its operand and pushes a 24-bit return address (minus one) to the stack.  RTL’s behavior is similar to that of RTS, except the former pulls a 24-bit address to return to the caller.  Succinctly, JSL - RTL works exactly like JSR - RTS, except for the size of the addresses being processed.

  2. Context change with COP - RTICOP is one of the 65C816’s two software interrupts, the other, of course, being BRKCOP’s original purpose was to provide a software hook for temporarily giving a co-processor control of the system.  As far as I know, there is no co-processor that is bus-compatible with the 65C816, which means COP is available for other purposes.  As with any other interrupt, the COP handler returns control with an RTI.

After cogitating on it for a while, I decided to go with method two.  While it would appear that COP would be a little slower in execution than JSL, the 816 uses eight clock cycles to process both instructions.  RTI, however, requires seven clock cycles, versus six for RTL, so there is a slight performance difference on the return.  Also, the COP front end has to figure out which function to run, which entails more computation than required to call an API function with JSL.  So overall, COP - RTI will be somewhat slower, although the performance difference can be partially effaced by running at high clock speeds.

A notable feature of using COP - RTI is applications don’t need to know the addresses of API functions in order to use them.  Instead, an API index, which is an integer, tells the COP front end which function is to be run.  If a new function is added to the API in the future, existing applications don’t need to be reassembled to work with the new BIOS, as long as the new API index is higher than all existing ones.  This feature helps with maintaining backward compatibility.

Functions can be made as transparent as is needed, since the COP handler’s front end will preserve machine state and the handler’s back end will restore it.  Instead of each function preserving and restoring the registers, the COP handler will take care of that.  If a function needs to return a value in one of the registers it can do so by rewriting the register value on the stack.

There are two ways to pass the API index to the COP handler.  One is to load a register with the index, and the other is to pass the index in the signature byte associated with COP.  Here again I weighed the pros and cons.  Using a register makes for a simpler COP handler front end, especially if it is the accumulator that is used.  The index merely has to be doubled and copied to the X-register.  The front end would then execute JMP (COPTAB,X) or JSR (COPTAB,X) to select the appropriate function, where COPTAB is a 16-bit vector table.  This would be the least processing-intensive method, but will mean the accumulator (or other register) will not be available for passing parameters into the called function.

Passing the API index in the COP signature is more complicated.  In order to fetch the signature, the return address and bank that were pushed when COP was executed would have to be retrieved, pointing the way to the signature.  Those values could be used to populate a direct page pointer, which then can be used in an LDA [<ptr>] instruction to load the signature.  As the return address on the stack is one byte past the signature, it must be decremented before being set up in the direct page pointer.

Alternatively, the bank can be loaded into DB (data bank register) and, again, the return address decremented to point at the signature.  That address would then be copied into the (16-bit) X-register so an absolute load can be performed to retrieve the signature.  The code for this procedure is a bit convoluted:

Code:
;===============================================================================
;
;icop: COPROCESSOR INTERRUPT SERVICE ROUTINE
;
icop     rep #%00110000        ;16-bit registers
         phb                   ;preserve machine state
         phd
         pha
         phx
         phy
;
;———————————————————————————————
;COP REGISTER STACK FRAME
;
cop_yrx  =1                    ;.Y (word)
cop_xrx  =cop_yrx+s_word       ;.X (word)
cop_arx  =cop_xrx+s_word       ;.A (word)
cop_dpx  =cop_arx+s_word       ;DP (word)
cop_dbx  =cop_dpx+s_mpudpx     ;DB (byte)
cop_srx  =cop_dbx+s_mpudbx     ;SR (byte)
cop_pcx  =cop_srx+s_mpusrx     ;PC (word)
cop_pbx  =cop_pcx+s_mpupcx     ;PB (byte)
;———————————————————————————————
;
         lda !#kerneldp
         tcd                   ;set kernel’s direct page
         cli                   ;resume IRQ processing
         lda cop_pcx,S         ;get return address from stack
         dec A                 ;point at signature
         tax                   ;use as index
         sep #%00100000        ;8-bit accumulator
         lda cop_pbx,S         ;get caller’s bank from stack
         pha                   ;make it a...
         plb                   ;temporary data bank
         lda \2$0,x            ;fetch signature...
;
;   —————————————————————————————————————————————————————————————————
;   The above instruction must be assembled with absolute addressing.
;   Otherwise, it will attempt to fetch the COP signature from bank
;   $00, ignoring the bank loaded into DB.  The \2 operator forces
;   the assembler to generate LDA $0000,X.
;   —————————————————————————————————————————————————————————————————
;
         phk                   ;kernel’s program bank
         plb                   ;now kernel’s data bank
         rep #%00100000        ;16-bit accumulator
         and !#%11111111       ;squelch noise in MSB
         beq icoperr           ;API index is 0, error
;
         dec                   ;zero-align API index
         cmp !#maxapi          ;number of defined API functions
         bcs icoperr           ;API number out of range
;
         asl                   ;convert API index to...
         tax                   ;jump table offset
         jmp (apifntab,x)      ;run API function

   ...etc...

The above is in Kowalski assembler syntax—the !# operator means to load a 16-bit immediate value.

The gain in passing the API index in COP’s signature is all registers are unencumbered and thus may be used for parameter passing.  I decided that unencumbered registers is of more value than slightly faster front-end processing.  So the procedure will be to pass the index in the signature.  Also, doing so lends itself well to API calls being wrapped up in macros.

Speaking of parameter passing, that can be done via the registers, via the stack, or via both.  Returning results to the caller can be done through the registers or by rewriting the data pointed to in a stack frame.  In the present BIOS, all APIs receive parameters and return results via the registers.  In most cases, this aspect of the BIOS could remain unchanged.  However, any API call in which one or more parameters is a pointer to data would have to be reworked to recognize 24-bit addresses, since the data could be anywhere in the MPU’s 16 MB address space.  That gives rise to an interesting design consideration.

Experience in writing code for the 65C816 has demonstrated that 24-bit values are awkward to process, especially when carrying out pointer arithmetic.  In developing my new-and-improved 65C816 string library, I elected to use 32-bit pointers so pointer manipulation would be more efficient.  By treating a pointer as a 32-bit entity, arithmetic and other operations don’t entail constantly using REP and SEP to change register sizes according to which piece of the pointer is being handled.  Despite a 16-bit fetch or store using an extra clock cycle over an eight bit operation, the overall speed at which pointer manipulation can be carried out is better, as executing REP and SEP isn’t part of the procedure.  The only real penalty with using 32-bit pointers is slightly greater direct page consumption.

The wrinkle with using 32-bit pointers is in passing them to a function.  It’s possible to pass one such pointer using the index registers, viz:

Code:
         rep #%00010000        ;16-bit index registers
         ldx !#addr & $ffff    ;address LSW
         ldy !#addr >> 16      ;address MSW

   ...etc...

Again, the above is in Kowalski assembler syntax.  The >> 16 operation is a 16-bit right-shift of the ADDR operand, which results in .Y being loaded with bits 16-31 of ADDR.

In the present BIOS, all but one of the functions that takes pointers to data as input requires only one such pointer, which makes the above method practical for those functions.  The exception is the SCSI command processing primitive, which needs two pointers, one pointing to the command descriptor block (CDB) and the other pointing to a buffer, e.g., a disk block buffer.  There are three possible ways to handle this.

  1. Create a separate API call to set the buffer address, if needed.  Following that operation, the SCSI command processor API can be called by loading the address of the CDB in .X and .Y, as above, and the target SCSI device ID in the accumulator.  Some SCSI commands don’t involve use of a buffer, so the first step could be skipped in those cases.  No stack processing would be required to get the parameters.

    The downside is two API calls would be required to process read and write operations on the target SCSI device.  As such operations are the ones most used, an execution speed penalty would result.

  2. Use a data structure containing the required parameters.  The structure would contain the CDB pointer, buffer pointer and a pointer to the target device ID.  The SCSI command processor code would get what it needs from the structure.  As with the first method, no stack processing would be required to get parameters.

    The downside is the caller has to do more work to set up the API call.

  3. Use a stack frame to pass parameters.  Such a call would be something like the following:

    Code:
             pea #buf & >> 16      ;buffer address MSW
             pea #buf & $ffff      ;buffer address LSW
             pea #cdb & >> 16      ;command descriptor block address MSW
             pea #cdb & $ffff      ;command descriptor block address LSW
             pea #scsiid           ;target SCSI device bus ID
             cop #kscsicmd         ;call SCSI command processor
             bcs error

       ...etc...

    The above takes care of the pointer requirements, as well as identifying the SCSI device that is the target of the operation.  Only one API call is needed, which eliminates the overhead of a second call to set the buffer address.

    The downside is processing will be more complicated due to stack addressing, and the SCSI command processor code would have to perform stack housekeeping prior to returning to the caller.

I have not yet decided which of the above methods I will use—each has its own complications.

One last thing.  As COP is an interrupt, it has a useful hardware effect.  During the seventh and eight clock cycles of processing COP, the MPU’s VPB (vector pull) output will go low.  In a system that has privilege levels, the glue logic can react to VPB by relaxing restrictions, mimicking the user/supervisor modes of the Motorola 68K.

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


Last edited by BigDumbDinosaur on Mon Nov 06, 2023 9:25 am, edited 2 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 20, 2021 4:36 am 
Offline

Joined: Sat Dec 30, 2017 3:19 pm
Posts: 116
Location: Detroit, Michigan, USA
The BIOS I've built for my 65816 systems also uses COP, with the signature byte being the API index and parameters passed on the stack, though simple calls like CONSOLE_WRITE just use a register.

The big issue I've found so far with the use of stack frames is that to use stack frame parameters, my dispatch sets up the direct page to point to the stack frame. Unfortunately that locks me out of using the BIOS's private DP.

One other possible approach instead of stack frames is GS/OS-style calls, where there is a pointer to a parameter directly after the API call. It would look something like this:

Code:
COP CONSOLE_WRITELN
.FARADDR string_ptr


The parameter in this case is just the string address, but it could easily be a pointer to a well-defined struct containing all the information needed by the API call. The advantage here is that you always know exactly how much data is being passed in right from the start, and that parameter address can be copied into the BIOS DP by the dispatch code instead of using a DP on the stack. Sure, you have to grab data from after the COP byte, but I'm already doing that to get the signature byte, and also in this case I'd lose all the stack cleanup code.

This method does give me warm fuzzy nostalgia feelings, and I'm actually considering switching to it before I get too much further along in development.


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 21, 2021 7:39 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8222
Location: Midwestern USA
jmthompson wrote:
The BIOS I've built for my 65816 systems also uses COP...The big issue I've found so far with the use of stack frames is that to use stack frame parameters, my dispatch sets up the direct page to point to the stack frame. Unfortunately that locks me out of using the BIOS's private DP.

After some review of what I developed for older versions of POC, it became clear that all API functions except the SCSI command primitive can be called with parameters loaded into the registers. The SCSI command primitive needs two (32-bit) pointers, plus the SCSI ID of the target device. I decided the way to do this is to call the function with the X- and Y-registers pointing to a structure that contains the command descriptor block and buffer pointers, with the (8-bit) device ID loaded into the accumulator. The only return from the command primitive is an error code in .A if something goes awry, indicated with carry set on return. This method will eliminate the need for stack housekeeping.

Quote:
One other possible approach instead of stack frames is GS/OS-style calls, where there is a pointer to a parameter directly after the API call. It would look something like this:

Code:
COP CONSOLE_WRITELN
.FARADDR string_ptr

The parameter in this case is just the string address, but it could easily be a pointer to a well-defined struct containing all the information needed by the API call.

That is a programming style I prefer to avoid. I don't like in-line data like that, as it interrupts code disassembly. Also, it necessarily forces static assembly of the data block. If the data will not be known until run-time, self-modifying code becomes necessary.

As I said above, all API functions, other than the SCSI primitive, are such that parameters can be passed in the registers. I won't have to do anything hinky to make it work.

Quote:
The advantage here is that you always know exactly how much data is being passed in right from the start, and that parameter address can be copied into the BIOS DP by the dispatch code instead of using a DP on the stack. Sure, you have to grab data from after the COP byte, but I'm already doing that to get the signature byte, and also in this case I'd lose all the stack cleanup code.

This method does give me warm fuzzy nostalgia feelings, and I'm actually considering switching to it before I get too much further along in development.

As some point, I am going to write a kernel that can be loaded from disk at boot time—I've already got working code for the boot—and I will have to settle how I will do the kernel API calls. I am leaning toward the UNIX/Linux convention of loading the API index into a register and pushing parameters to the stack before making the call. Doing it that way instead of using COP's signature as the API index cuts down on API front end gyrations. Given the relative ease at which the 65C816 handles the stack, I think that will give me the best flexibility. I discuss this technique in some detail in my 65C816 interrupt article.

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


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 21, 2021 8:12 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10829
Location: England
Sounds like a much better approach! Those signature bytes are tempting, but clumsy.


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 21, 2021 8:32 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8459
Location: Southern California
BigEd wrote:
Those signature bytes are tempting, but clumsy.

I haven't given it much thought; but I think you could do:
Code:
        LDY  #1
        LDA  (1,S),Y  ; Get the first byte after the return address.

Fixing the return address, if necessary, might be something like
Code:
        LDA  1,S      ; Get the return address,
        CLC
        ADC  ____     ; increment it by a constant, or by something found
        STA  1,S      ; in the parameter data field, and put it back.

It's so much easier on the '816 than it is on the '02.

_________________
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  
PostPosted: Sat Aug 21, 2021 9:39 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8222
Location: Midwestern USA
BigEd wrote:
Sounds like a much better approach! Those signature bytes are tempting, but clumsy.

That is true.

Regardless of which route you take to pick up the signature, you have to either monkey with DB (data bank) or set up a long pointer on direct page. Fetching the bank and address that was pushed when COP was serviced is easy enough, but changing DB is awkward because the only access to it is through the stack. Use of the long pointer makes fetching the signature bank-agnostic, but it means yet another piece of direct page has to be used.

GARTHWILSON wrote:
I haven't given it much thought; but I think you could do:
Code:
        LDY  #1
        LDA  (1,S),Y  ; Get the first byte after the return address.

That won't work unless you load DB with what was in PB (program bank) at the time of the interrupt. Otherwise, the LDA (1,S),Y instruction will fetch from the data bank that was in context at the time COP was executed. That might not be the bank in which the COP instruction and its signature are located.

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


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 21, 2021 8:59 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8459
Location: Southern California
BigDumbDinosaur wrote:
That won't work unless you load DB with what was in PB (program bank) at the time of the interrupt. Otherwise, the LDA (1,S),Y instruction will fetch from the data bank that was in context at the time COP was executed. That might not be the bank in which the COP instruction and its signature are located.

Ah, yes, I was forgetting operation in different banks. It can still be done of course, but takes more instructions.

_________________
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  
PostPosted: Sat Aug 21, 2021 10:26 pm 
Offline

Joined: Sat Dec 30, 2017 3:19 pm
Posts: 116
Location: Detroit, Michigan, USA
BigDumbDinosaur wrote:
That is a programming style I prefer to avoid. I don't like in-line data like that, as it interrupts code disassembly. Also, it necessarily forces static assembly of the data block. If the data will not be known until run-time, self-modifying code becomes necessary.


That is a good point, and one I had not considered. The Apple IIGS monitor automatically formats these things properly during disassembly, so back in the day it was never much of an issue. I also thought about it more and decided against this technique for reasons similar to what you mention below.

Quote:
As I said above, all API functions, other than the SCSI primitive, are such that parameters can be passed in the registers. I won't have to do anything hinky to make it work.


And this is why I decided against doing inline data myself. I started thinking about all my console/serial calls that read or write a single byte using just the accumulator, and realized to make things work like I was thinking I'd need to add an unnecessary four-byte pointer after those calls. That's just a big waste of ROM space.

I think now I'm going to do a hybrid approach; I'm going to keep my existing COP dispatcher, but only allow either 0 bytes or 4 bytes to be put on the stack. If the four bytes are present I will copy them to a location on the BIOS direct page, which will be what D is set to inside the BIOS at all times.

Quote:
As some point, I am going to write a kernel that can be loaded from disk at boot time—I've already got working code for the boot—and I will have to settle how I will do the kernel API calls. I am leaning toward the UNIX/Linux convention of loading the API index into a register and pushing parameters to the stack before making the call. Doing it that way instead of using COP's signature as the API index cuts down on API front end gyrations. Given the relative ease at which the 65C816 handles the stack, I think that will give me the best flexibility. I discuss this technique in some detail in my 65C816 interrupt article.[/color]


I do love that article on your page; it's actually what inspired my current design. :)

Loading from storage is definitely a good way to go as it saves on EEPROM/flash write cycles. :) I can't really do that on my current build because I only have 32K of RAM, so for now the BIOS and eventually the OS will sit in ROM, which is also 32K. On my next build though I might do something like that, as it'll have a meg of RAM.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 564 posts ]  Go to page Previous  1 ... 29, 30, 31, 32, 33, 34, 35 ... 38  Next

All times are UTC


Who is online

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