Jump to content

svenvandevelde

Members
  • Posts

    461
  • Joined

  • Last visited

  • Days Won

    17

Posts posted by svenvandevelde

  1. On 11/21/2022 at 8:53 AM, TomXP411 said:
    On 11/21/2022 at 5:13 AM, svenvandevelde said:

    So what is about this jsrfar?

    JSRFAR does basically the same thing your code does, it's just more complicated because it needs to be more general purpose.

    I'm sorry but this feedback is not completely correct. It will run in main memory but cannot work in a bank. When calling code from bank in an other bank, jsrfar must be used 😞

  2. On 11/21/2022 at 7:07 AM, Stefan said:

    Lets say the PC is at 1:a000. The code does an LDA #2 and then STA 0. The PC is now a004. What happens next? I would say execution continues at 2:a004, and that the bank 1 code immediately loses control

    Now I get it! That is an answer. So when in bank 1, you cannot switch to bank 2 easily running code in bank 1. Once sta 0 is executed with accumulator value 2, the jsr will never be executed... damn .... complicated! This needs a much better explanation on the cx16 manual!

  3. Also .. why is there one jsrfar routine in the kernal and no two? One for calling ram banked code and one for calling rom banked code? It would save some branches and logic. Also would add one input parameter whether to preserve registers or not ... like setting the decimal flag and if set, not register preservation using stack would be done in jsrfar? I mean, when you call normal kernal routines, registers are neither preserved ... or maybe in doing understand the jsrfar enough ...

  4. On 11/21/2022 at 8:53 AM, TomXP411 said:

    JSRFAR does basically the same thing your code does, it's just more complicated because it needs to be more general purpose. 

    Your code works fine for one address, but when you need to JSR to different addresses, you duplicate a lot of code by repeating the stack push/pull every time. However, for performance, you may need to do just that.

    So the method I outlined earlier is not suitable for assembler generated by a compiler as easily inline bank hopping logic won't work. 

  5. I need to start moving code into banks as lots of low memory gets exhausted. Im hitting the $9200 boundary already...

    This code, for example, addresses routines like a hash table that is called 60 times multiplied by the amount of objects on the canvas., per second. Or level specific code to move objects over the screen in specific patterns or behave in specific ai logic. 

    So yes it needs to be fast. 

    There are also slower routines possible like level loading which can take more time. But I don't really need jsrfar at all(I think).

    A call into rom is simply setting zp 1 and jsr the rom function address, which works well. Upon rts I just reset the zp 1.

    A call from one RAM bank to an other RAM bank is perfectly possible by setting zp 0 and then jsr the address. Upon rts I just reset the zp 0.

    So what is about this jsrfar?

  6. On 11/16/2022 at 1:49 PM, Stefan said:

    You would at least need some bridge or trampoline function in low RAM when calling code from one bank in another bank.

    And this jumping between banks is too be avoided. But jumping from low ram to code in a bank and back should not be such a hard thing to do, right?

  7. It all comes down to selecting which of the routines you would position as "far" and which routines need to be kept in low memory.
    Also to run code in banks, this requires to have the data used by the code in the bank to be "in" the bank.

    So running code in a bank requires to have the data for the code either in the same bank, or in an other bank.

    In other words, having a logic that runs in ram banks, and switching between ram banks can become very tricky.
    But if the code and the data to run the code is in the same bank or in low memory, then it should be fine, right?

  8. Hello all,

    In this article I would like to spend some time with you on some C coding techniques, so that your C program uses the 65C02 CPU most efficiently.
    The principles outlined below also apply to other languages (probably).

    The subject of this thread is about how to combine arrays and structure C language constructs.
    There are two distinguished ways how arrays and structures can be combined, which are known in the software world as Arrays of Structures (AoS) compared to Structure of Arrays (SoA) .

    Please see below the following example:

    image.png.5c3ccb7f875ad6bc62e418c7f0975738.png

    The above compares two different ways of how Arrays and Structures can be combined. On the left, the AoS variable is an Array of Structures.
    The type definition struct_t only contains scalar data types, while in the variable declaration, AoS is dimensioned as an Array (of Structures).

    On the right side, you see the other alternative, where the SoA variable is a Structure of Arrays.
    The type definition struct_t contains the arrays inside the struct (thus, a Structure of Arrays), with the variable declation of SoA being a simple structure declaration.

    The initialization of the data contents between AoS and SoA is different, the AoS fills out the structure completely one by one, while the SoA fills out the field array completely within the structure.
    As a result, observe the memory layout in the ligher boxes on the picture, how different the memory is arranged between AoS and SoA. Both variables have the same total size, but the bytes have a different order.

    And this is very important regarding the 6502 CPU addressing modes. The 6502 supports various addressing modes, which you can find documentation here on the X16 forums, and all over the internet, just google.
    Since structures are addressed in a linear mode in memory, the most optimal addressing mode to be used are the absolute,x or absolute,y addressing modes (if possible).
    Of course, absolute,x or y addressing can only be used if the location of the structure is fixed in memory, otherwise indirect indexed addressing or other types of addressing could be used (indexed indirect or self modifying?).
    However, indirect indexed addressing modes should be avoided as much as possible to my experience, as these constructs clutter code and also consume much more cycles, but more on that in an other post when we discuss pointers and further deep dive into this subject of AoS versus SoA.

    Let us first compare how the C compiler generates the assembler, utilizing the AoS versus SoA variables, in a simple example below.

    image.png.65def96360de70c47a198106b4893fcc.png

    The examples speak for itself, so let us focus on the resulting assembler, because there is a clear distinction!
    Note that in this example, both variables are addressed using the index variable, which is an 8-bit value (char).
    This is important, since the 6502 absolute,x and absolute,y addressing modes require the x or y register to be populated with an 8-bit value.
    Using 16-bit index values to address arrays is possible, but through completely other assembler contructs (see in later post).

    In the AoS case, the C compiler has chosen to use the x register for the absolute indexed addressing. Although this solution looks elegant,
    one can observe that the total assembler code required is a bit longer than the SoA variation.

    But more importantly, the "reach" of the x register to address the AoS array is far more limited than in the SoA case.
    The AoS variable can only address 51 elements  (256 divided by 5 bytes, floored), while the SoA variable is able to address each field individually,
    - 128 elements, in case the field has a type containing 2 bytes, like pointers, int,
    - 256 elements for 8 bit data types like char
    !
    - 64 elements if your variable is a 4 bytes in size, like the type long ... (not recommended on 8bit computers).

    This is a major distrinction. To create data structures for C programs using the 6502 CPU, the Structure of Arrays technique is a preferred technique,
    compared to Arrays of Structures.

    Hopefully this topic is useful for others. Please leave your comments or remarks regarding this subject.
    If you have other ideas or other experiences you want to share about this topic, by all means, feel free to do so for others to pick this up and make better software for the X16.
    I might edit this first post, and update the text here with your reference if the addition is very useful and adds to the subject.

    Thanks,

    Sven 

     

     

    • Like 2
  9. On 11/1/2022 at 10:37 PM, neutrino said:

    One issue could be interrupts or NMI occurring while a different bank is switched in than the expected one.

    For basic in the vanilla x16 setup? For ram banks? Why would an nmi or other interrupts interfere with basic code like this. I would say that these interrupt routines should be agnostic to basic, no?

  10. On 10/31/2022 at 8:47 AM, TomXP411 said:

    Can we even trust a bank switch in BASIC?

    Please allow me to quote on this. Since the commander x16 is for educational purposes, it is a requirement that bank switching in basic of the RAM banks needs to be allowed. Functions should be available to allow this switching and it should be reliable. The banks are a key selling point for the x16.

  11. On 10/30/2022 at 12:34 PM, Manannan said:

    I have a simple question.

    In terms of cycles wasted, is switching banks an expensive operation, or does it happen very quickly?

    Switching banks is not really expensive. It takes one read in various ways and one write to zero page. Typically in functions, you'll want to set the bank and upon return restore the previous bank. You can do this using the stack.

    routine: lda $00

    pha

    lda bank

    sta $00

     

    Upon return...

    pla

    sta $00

    rts

     

    So ... not so expensive actually.

    It creates a bit of planning using the banks but that's the fun part of it all ...

    Sven

×
×
  • Create New...

Important Information

Please review our Terms of Use