6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun May 05, 2024 6:17 pm

All times are UTC




Post new topic Reply to topic  [ 58 posts ]  Go to page Previous  1, 2, 3, 4  Next
Author Message
PostPosted: Sun Feb 22, 2015 6:07 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8173
Location: Midwestern USA
BigEd wrote:
granati wrote:
BigDumbDinosaur wrote:
granati wrote:
... coding parameters "online" (i.e.: a static string, null terminated, that follow the COP instruction).
... As for in-lining data in a program, that is a Commodore-inspired aberration that I hope to never encounter. :lol:


Ehehehe....right....aberration.... Apple too inspired this aberration...

Acorn did the same thing, both with a routine to print out a string following a JSR (surely quite a common trick) and also BRK.

You're correct, of course, so I'll shift some of the blame away from Commodore. :lol:

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


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 22, 2015 6:20 pm 
Offline
User avatar

Joined: Wed Feb 13, 2013 1:38 pm
Posts: 586
Location: Michigan, USA
Dr Jefyll wrote:
Self modifying code...
  • must necessarily reside (or be copied into) RAM
  • has the potential to be confusing, but only if the author hasn't bothered to include adequate comments
These issues hardly make its usage a sin. I suspect theGSman was being humorous, although some folks do have strong feelings on the subject. IMO it's a just another tradeoff, worthwhile in some circumstances but not in others.

-- Jeff

Thank you, Jeff. I'm aware of those caveats...

My apologies to the group for drifting off-topic...


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 22, 2015 6:26 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8173
Location: Midwestern USA
theGSman wrote:
BigDumbDinosaur wrote:
As for in-lining data in a program, that is a Commodore-inspired aberration that I hope to never encounter. :lol:

??? What's wrong with in-lining constants? That is standard practice in Forth (using (LIT) or (SLIT) ). In-lining variable data (self modifying code) is, of course, a sin.

Actually, standard Forths go a little too far by combining the name, code and data in the same word. It makes for efficient allocation of RAM but makes ROMming almost impossible. That's why my version separates all three.

A little bit of apples and oranges, no? Forth is a "high level" language if compared to assembly language. Many high level languages allow intermixing code and data, e.g., printf("This is a string literal.\n") in C. However, a C compiler will store the string literal This is a string literal.\n separately from the machine instructions that refer to it—the string would be stored in the .data section of the program, not embedded in the .code section.

However, let's not get too far off-topic, as this is supposed to be about 65C816 programming. :D

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


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 22, 2015 8:31 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8432
Location: Southern California
BigDumbDinosaur wrote:
A little bit of apples and oranges, no? Forth is a "high level" language if compared to assembly language. Many high level languages allow intermixing code and data, e.g., printf("This is a string literal.\n") in C. However, a C compiler will store the string literal This is a string literal.\n separately from the machine instructions that refer to it—the string would be stored in the .data section of the program, not embedded in the .code section.

However, let's not get too far off-topic, as this is supposed to be about 65C816 programming. :D

The 6502 is not efficient at reading inlined data and adjusting its return address to just past the data; but the '816 has the (sr,S),Y and friends which improve things a lot. In spite of the 6502's inefficiency at doing inlined data, programmer productivity and code maintainability improve when macros are used to assemble inlined data. The macros make for, in essence, a higher-level language. Without changing the code that uses the macros, the macros can be changed from inlining the data to storing the data separately from the machine instructions that refer to it. (I know BDD knows that, but it may further help develop ideas for whoever reads this in the archives years later. :D )

_________________
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: Mon Feb 23, 2015 7:45 am 
Offline

Joined: Mon Jun 24, 2013 8:18 am
Posts: 83
Location: Italy
BigDumbDinosaur

A little more explanation about the fact that i decided to catch the signature byte in COP handler.
- separation of bios calls and o.s. calls
- interface to floating point routines by the cop handler.
- more flexibility and efficiency changing the handler to a specific function (by changing the address on a table)

Even if one can reach any goal just with COP $XX and passing an index (ignoring $XX), i think is more clearly a logic separation of functions, assigning a signature to a specific field.

Marco

_________________
http://65xx.unet.bz/ - Hardware & Software 65XX family


Top
 Profile  
Reply with quote  
PostPosted: Mon Feb 23, 2015 7:22 pm 
Offline

Joined: Mon Jan 26, 2015 6:19 am
Posts: 85
BigDumbDinosaur wrote:
A little bit of apples and oranges, no? Forth is a "high level" language if compared to assembly language. Many high level languages allow intermixing code and data, e.g., printf("This is a string literal.\n") in C. However, a C compiler will store the string literal This is a string literal.\n separately from the machine instructions that refer to it—the string would be stored in the .data section of the program, not embedded in the .code section.

However, let's not get too far off-topic, as this is supposed to be about 65C816 programming. :D

I wouldn't have thought it was that far off topic. Any 65C816 project is likely to include a BASIC interpreter or FORTH system or both (the latter would be my choice). As such, any tricks that would make these languages work better on an '816 would be very relevant.

You are right though. Most C compilers would place constants in rodata rather than in-line them.


Top
 Profile  
Reply with quote  
PostPosted: Mon Feb 23, 2015 7:33 pm 
Offline

Joined: Mon Jan 26, 2015 6:19 am
Posts: 85
Michael wrote:
theGSman wrote:
... In-lining variable data (self modifying code) is, of course, a sin.


May I ask why, please?

Dr Jefyll wrote:
Self modifying code...
  • must necessarily reside (or be copied into) RAM
  • has the potential to be confusing, but only if the author hasn't bothered to include adequate comments
These issues hardly make its usage a sin. I suspect theGSman was being humorous, although some folks do have strong feelings on the subject. IMO it's a just another tradeoff, worthwhile in some circumstances but not in others.

-- Jeff

You could also add debugging difficulties and reusability of code in different programs as potential problems.

There are a number of programming strategies that fall into the "dontdoit" category (such as self-modifying code and spaghetti coding). If you are into commercial software then you would be best advised to keep code and data separate and adhere to a modular approach as far as practicable.

However, for personal purposes, anything goes. In cases where timing or memory availability is critical you might have to abandon politically correct programming techniques.


Top
 Profile  
Reply with quote  
PostPosted: Mon Feb 23, 2015 7:57 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8173
Location: Midwestern USA
granati wrote:
BigDumbDinosaur

A little more explanation about the fact that i decided to catch the signature byte in COP handler.
- separation of bios calls and o.s. calls
- interface to floating point routines by the cop handler.
- more flexibility and efficiency changing the handler to a specific function (by changing the address on a table)

Even if one can reach any goal just with COP $XX and passing an index (ignoring $XX), i think is more clearly a logic separation of functions, assigning a signature to a specific field.

Marco

I actually considered using software interrupts (COP) to call API functions in POC's BIOS, but scrapped the idea after looking at the amount of front- and backend code that was required to do it properly. Since the BIOS is in ROM, its location is fixed, which eliminates one of the principal reasons for using a software interrupt API. The API entry jump table can be made static, allowing the treatment of BIOS functions as subroutines instead of software interrupt service routines. Also, calling BIOS API functions via JSR is a lot less complicated for both the caller and the BIOS. As a bonus, execution is substantially faster, as the overhead involved in processing a software interrupt (especially stack activity) is completely avoided.

Execution speed becomes important when you consider that some API functions may be called hundreds or thousands of times in rapid succession. For example, the putcha function (put a character on TIA-232 channel A) would be called some 2000 times in a small fraction of a second to repaint the console screen. The call as I have it arranged is:

Code:
          lda #char             ;character to be printed
          jsr putcha            ;buffer it for transmission

Inside putcha, the registers are preserved and the usual operations required to buffer a byte into a FIFO are carried out, followed by register restoration and an RTS. It's simple, clean and fast.

If I were to do it with software interrupts as you describe, I would need front end code to get the COP signature from the bank in which the calling program is located (which involves copying the value of PB to DB after DB has been preserved), restart IRQs, check the signature for range, use it to index a table that tells the called function how many parameters to expect, and then use that index to look up the address of the function and go to it. All of that gets executes before the actual work of transmitting a character gets done. When the function was completed, it would have to go to a back end that would update the registers as required, and then execute return-from-interrupt steps. Just my opinion, but it's way too much work just to call a BIOS API, given that the BIOS is almost always running in ROM, which means it's static.

Now, in the case of an operating system kernel running in RAM, calling APIs via software interrupts makes sense, especially if attempting to implement memory protection—you wouldn't want to expose a jump table to user-mode applications. That is the model I will be using when I develop my 816NIX kernel. However, I still won't be using the COP signature. Every API in the kernel will take pointers to data, which pointers will be pushed to the stack prior to the API call. It's modeled on the method used in System V UNIX.

Consider a C program to create a new file:

Code:
    /* create & open a new file in ANSI C */

    char fname[] = "/usr/bdd/newfile"; /* pathname */

    int main() {
        int fd;                        /* file descriptor */
        fd = creat(fname,0664);        /* create & open file */ }
        return(fd);                    /* return file descriptor to caller */
    }

If the above were compiled on an MC68000 system running the System V UNIX kernel you might get:

Code:
* machine code generated in main()...
*
         move #$01b4,(sp)      * push mode to stack
         move #$41d7,-(sp)     * push pathname pointer to stack
         jsr creat             * call creat API library code
*
*
* creat() kernel API call library machine code...
*
creat    moveq #$08,d0         * load register D0 with creat API index ($08)
         trap #$00             * invoke kernel API
         bcs _error_           * if error, branch w/error code in D0
*
         rts                   * file created & opened, file descriptor in D0
*
_error_  ...handle error processing...

The pathname pointer ($41D7) is a made-up value.

The equivalent on a 65C816 system using a System V UNIX API model would be:

Code:
;machine code generated in main()...
;
         pea #$01b4            ;push file mode to stack
         per $41d7             ;push pathname pointer
         jsr creat             ;call creat() library function
;
;
;creat() kernel API call library machine code...
;
creat    sep #%00100000        ;select 8 bit accumulator
         lda #$08              ;create() API index
         cop $00               ;transfer execution to kernel
         bcs _error_           ;kernel API returned an error
;
         rts                   ;file created & opened, file descriptor in .A
;
_error_  ...handle error processing...

Note that TRAP #$00 in the MC68000 code is the analog of COP $00 in 65C816 assembly language. Like the '816, the 68K supports multiple trap numbers, but they are not used. Instead, the folks who wrote the kernel API library for use on the 68K decided to stick with what had been earlier implemented on the DEC PDP-11, which was to use a general purpose register to pass the API number to the kernel. It turned out to be more succinct that using the 68K's TRAP #$00 vectoring capability.

In the case of the 65C816 model, passing the API index number in a register completely eliminates the work required to load the signature, excising many clock cycles out of the API front end. Unlike the MC68000, whose TRAP instruction is vectored in hardware according to the trap number (which feature is not used in System V UNIX and Linux implementations), COP is not (it sees only the vector at $00FFE4 in native mode), which means your code has to synthesize what the 68K does internally. Makes sense to me. :D

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


Top
 Profile  
Reply with quote  
PostPosted: Mon Feb 23, 2015 8:29 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8173
Location: Midwestern USA
theGSman wrote:
BigDumbDinosaur wrote:
A little bit of apples and oranges, no? Forth is a "high level" language if compared to assembly language. Many high level languages allow intermixing code and data, e.g., printf("This is a string literal.\n") in C. However, a C compiler will store the string literal This is a string literal.\n separately from the machine instructions that refer to it—the string would be stored in the .data section of the program, not embedded in the .code section.

However, let's not get too far off-topic, as this is supposed to be about 65C816 programming. :D

I wouldn't have thought it was that far off topic. Any 65C816 project is likely to include a BASIC interpreter or FORTH system or both (the latter would be my choice). As such, any tricks that would make these languages work better on an '816 would be very relevant.

As our discussion is on 65C816 assembly language, what one might do in BASIC or FORTH doesn't necessarily apply. If either of those is implemented as an interpreter running from ROM, then the code is by definition static and hence in-lining data tables or character strings represents a "no harm done" scenario.

However, in most operating environments, programs execute from RAM, and the code and data sections of a machine language program are kept separate as a matter of style and also as a part of environment protection. For example, the UNIX 0410 a.out model prohibits read/write activity on the code section except during opcode and operand fetches. In other words, the 0410 a.out model cannot be written to use in-line data. UNIX also supported a 0407 a.out model, which did allow in-line data. The 0410 a.out was developed to allow multiple processes to share a single program image, which potentially reduced the amount of RAM that was being consumed. Other than during opcode and operand fetches, processes were prohibited from accessing the program image—doing so would cause an immediate memory fault and core dump.

Quote:
You are right though. Most C compilers would place constants in rodata rather than in-line them.

This style became important when the concept of linked libraries was introduced to the UNIX world 30-odd years ago.

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


Top
 Profile  
Reply with quote  
PostPosted: Mon Feb 23, 2015 8:32 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8173
Location: Midwestern USA
theGSman wrote:
However, for personal purposes, anything goes.

Naturally. If it blows up and you are the only one who is affected, no harm done.

Quote:
In cases where timing or memory availability is critical you might have to abandon politically correct programming techniques.

I don't think referring to proven coding techniques as "politically correct" is accurate. That's akin to referring to proven piloting techniques in an airliner as "politically correct."

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


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 24, 2015 3:52 am 
Offline

Joined: Mon Jan 26, 2015 6:19 am
Posts: 85
BigDumbDinosaur wrote:
As our discussion is on 65C816 assembly language, what one might do in BASIC or FORTH doesn't necessarily apply.
What one might do in BASIC or FORTH is tangential to the issue that they are both written in assembly language.


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 24, 2015 4:05 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8432
Location: Southern California
and Forth allows dipping into assembly language at any point, even inside a high-level definition, even in an indirect-threaded model, using INLINE...END-INLINE . It's more common though to write primitives when you need assembly's performance and total control.

_________________
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: Tue Feb 24, 2015 6:07 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8173
Location: Midwestern USA
GARTHWILSON wrote:
and Forth allows dipping into assembly language at any point, even inside a high-level definition, even in an indirect-threaded model, using INLINE...END-INLINE . It's more common though to write primitives when you need assembly's performance and total control.

Forth is fairly unusual in that respect. While it is possible in many cases to insert assembly language into C source code and have the compiler accept it, it won't necessarily end up where you might expect. Plus if the assembly language includes references to a data table that is also written in assembly language, that table will become part of the program's data section, not the code section.

This is all substantially off-topic at this point. :D

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


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 24, 2015 8:16 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10798
Location: England
BDD, if your BIOS API is a JSR/RTS, doesn't that mean the BIOS can only be called from bank zero? Is that a consequence of your OS architecture?


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 24, 2015 10:00 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
My take on the software interrupt vs subroutine call API for kernal routines is that SWIs make more sense when accessing kernal functions in multi-tasking operating systems as the state of the caller will be placed on the stack ready for a task switch.

On a *IX system it is usually only the core functions listed in section (2) of the man pages that invoke SWIs (e.g. open, read, write, close, fcntl, etc.) as these calls will typically take a long time to complete (in CPU terms) during which time that task will sleep. The routines listed in section (3) are usually implemented as plain library code (e.g. ctype and string operations) or only call kernal functions occaisionally (e.g. buffered I/O like fopen, fclose, fget, fput, etc.).

So if your 65xx operating system is inherently single user then a subroutine call based API is probably the simplest and fastest approach (like the BBC's MOS) but it you want to multi-task then some of API should eventually execute a BRK to invoke the core operation (e.g. fork, exec, exit, open, close, etc.).

My emulators use COP exclusively to access virtual hardware - they are all frontended by a coprocessor in effect. This simplifies my address decoding as I only have to resolve addresses to either RAM or ROM which speeds things up.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 58 posts ]  Go to page Previous  1, 2, 3, 4  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: