5th of July: Visiting old Friends…

My last public “announcement” was, that I wanted to play some games on PS4. I did that, I played thru The Last of Us (Part 1 and 2). Great games IMHO.

I bought a new computer (my iMac is slowly falling apart) – and will probably continue to play some games which I couldn’t play before – my first new computer since over 10 years – I have some catching up to do!

Nonetheless I am near the baltic sea again and my new computer is at home, and here I am sitting in front of my laptop and think about “the old days”.

This is what THIS is about:

Ok. Forget that on my Laptop I have not configured Vide correctly – and all vectors are open (and I don’t have openGL)…

What you might discern though is a “BANK 0” in the middle!

This is my first test to do bankswitching in “C”! – The “BANK 0” is output in bank 0 (what a surprise) and the rest is as usual printed in bank 1.

In the following I will go into detail how I did that. At one stage this will be automated in Vide – but not yet – for now it is manual “labour”.
(And the current stage will give Peer the creeps – because I didn’t do it “right”. But you know me – this started as a proof of concept, and now that it is working – it will not change soon 🙂 ).

In order to achieve bankswitching a couple of thing must be ensured/considdered:

  • there ARE two banks (64k)
  • the switching of banks must be done “supervised” – in a controlled known “stable” area

TWO BANKS

For now this is ensured by building two different “bin” files. At least the bank 0 – must be exact 32768 bytes long.
The second bank must be concatinated to the first bank. This can be manually done in VIDE using the
Tools->Utilities->File utility
(and I am sure there are Unix/Windows commands that can also be used).
The Bank 1 must start at exactly the offset 32768!

SWITCHING

The switching must be done in “neutral” zones. These are areas of the program which are EXACTLY the same for both banks. For the time being I chose the “beginning” of each bank.
My first try for this was to use the section “.cartridge” – define the bankswitching functions within that region.
This is not possible using GCC – since that section contains per default “data”. When I put a function within that section, than gcc replies with an error: “… causes a section type conflict”. This can not be easily circumvented.
The “cleanest” way to circumvent this would probably be to define a “.cartridgeBoot” section, which would start right after the “.cartridge” section. This is probably what Peer would have (me) done.

But in order to do that I had to build a new CRT0 file – and actually I wanted to just “test it out” and not build a new “C” environment.

After a couple of tests – I came up with the following.
Still using the “cartridge.c” file – but adding “direct” assembler.

This is the source:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
// ***************************************************************************
// cartridge
// ***************************************************************************
// This file was developed by Prof. Dr. Peer Johannsen as part of the 
// "Retro-Programming" and "Advanced C Programming" class at
// Pforzheim University, Germany.
// 
// It can freely be used, but at one's own risk and for non-commercial
// purposes only. Please respect the copyright and credit the origin of
// this file.
//
// Feedback, suggestions and bug-reports are welcome and can be sent to:
// peer.johannsen@pforzheim-university.de
// ---------------------------------------------------------------------------
#include <vectrex.h>

// ---------------------------------------------------------------------------
// vectrex cartridge init block 

struct cartridge_t {
    char copyright[11]; // copyright string, must start with "g GCE" and must end with "\x80"
    const void * music; // 16 bit memory adress of title music data
    signed int title_height; // signed 8 bit value, height of game title letters
    unsigned int title_width; // unsigned 8 bit value, width of game title letters
    int title_y; // signed 8 bit value, y coordinate of game title
    int title_x; // signed 8 bit value, x coordinate of game title
    char title[]; // game title string, must end with "\x80\x00"
};

// ---------------------------------------------------------------------------
// edit here to set game title

const struct cartridge_t game_header __attribute__((section(".cartridge"), used)) = {
    .copyright = "g GCE 2020\x80", // change year if neccessary, do not change "g GCE"
    .music = & Vec_Music_1, // taken from included headers
    .title_height = -8,
    .title_width = 80,
    .title_y = -16,
    .title_x = -72,
    .title = "AKLABETH\x80" // note that \x00 is automatically appended!
};

#define __ass asm volatile

__asm(
    " jmp _endOfHeading\n"
);

void bank0functionTest(void);


#define NUMBER_OF_FUNCTIONS 10
const void *
    const bankFunctions[NUMBER_OF_FUNCTIONS] __attribute__((section(".cartridge"), used)) = {
        (void * ) bank0functionTest,
        (void * ) 1,
        (void * ) 1,
        (void * ) 1,
        (void * ) 1,
        (void * ) 1,
        (void * ) 1,
        (void * ) 1,
        (void * ) 1,
        (void * ) 1
    };

// idea for parameters
//
// all passed parameters use
// unused BIOS RAM locations
// they get loaded before the call
// and reread after the call


__asm(
    ".setdp 0xd000,_DATA\n"
    ".globl _subBank0\n"
    "_subBank0:\n"
    " lda      #0xDF                          ; Prepare DDR Registers % 1101 1111 1111 1111\n"
    " sta      *_VIA_DDR_b                     ; all ORB/ORA to output except ORB 5, PB6 goes LOW\n"
    " lda      #0x01                          ; A = 0x01, B = 0\n"
    " sta      *_VIA_port_b                    ; ORB = 0x1 (ramp on, mux off), ORA = 0 (DAC)\n"
    " ldx      #_bankFunctions\n"
    " lslb\n"
    " ldx b,x\n"
    " jsr      ,x\n"
    " lda      #0x9F                           ; Prepare DDR Registers % 1001 1111 1111 1111\n"
    " sta      *_VIA_DDR_b                     ; all ORB/ORA to output except ORB 5, PB6 goes LOW\n"
    " rts\n"

    "_subBank1:\n"
    " lda      #0x9F                           ; Prepare DDR Registers % 1001 1111 1111 1111\n"
    " sta      *_VIA_DDR_b                     ; all ORB/ORA to output except ORB 5, PB6 goes LOW\n"
    " ldx      #_bankFunctions\n"
    " lslb\n"
    " ldx b,x\n"
    " jsr      ,x\n"
    " lda      #0xDF                          ; Prepare DDR Registers % 1101 1111 1111 1111\n"
    " sta      *_VIA_DDR_b                     ; all ORB/ORA to output except ORB 5, PB6 goes LOW\n"
    " lda      #0x01                          ; A = 0x01, B = 0\n"
    " sta      *_VIA_port_b                    ; ORB = 0x1 (ramp on, mux off), ORA = 0 (DAC)\n"
    " rts\n"

    "_endOfHeading:\n"
);

// ***************************************************************************
// end of file
// ***************************************************************************

Everything from line 42 to line 106 is “new” and needed for bankswitching support.

The “cartridge.c” file in both “banks” must be “nearly” equal.
“Nearly” means – they must be the SAME size, everything must be at exactly the SAME position. The only thing you are allowed to change is the contents (but NOT the length!!!) of the array “bankFunctions”.

In your “bank 0” definition here go all functions that should be accessable from bank 1.

In your “bank 1” definition here go all functions that should be accessable from bank 0.

In my example I only filled in one “test” function – and it is called “bank0FunctionTest” – and all it does is to print the “BANK 0” string:

1
2
3
4
5
6
void bank0functionTest(void)
{
    Intensity_a(MAX_BRIGHTNESS);                  /* set some brightness */
    Vec_Text_Width = 100;
    Print_Str_d(0,-70,"BANK 0\x80");            /* print hello world! */
}

The exact function name – and the address of the function is only known to “bank 0”.

Within bank 1 you do not have any information about its address or its name.
But since the function was put (manually) first in the function array – and even bank 1 “knows” that… we can call the bank 0 function via its index (in this case 0).
Thus the call from bank 1 looks like this (line 8):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
static void _MAINIntro(void)
{
	int buttonState = 0;
	int intensity = 0;
	while (1)
	{
		Wait_Recal();
		subBank0(0);
		if (intensity<120) intensity++;
		
		Intensity_a((unsigned int)intensity); // set intensity of vector beam...
		
		draw_synced_list_c((signed char *)TitleList,0,0,0x00,0x20);
...

What one probably should do is a “#define BANK0_FUNCTIONNAME 0” and than use that definition in the call – it makes the code more readable.

If in bank 0 you want to call a function from bank 1 – than exactly the same must be done –

  • use the subBank1(xxx) function (instead of the subBank0(xxx) function)
  • “declare” callable bank 1 functions in the “cartridge.c” file of bank 1.

The “subBankX” functions are the “usual” bank switching functions and are documented somewhere in Vide and this blog – so I will not repeat myself here.

All things considered – it was actually pretty straight forward and “easy” to add multiple banks for “C”.
It is a bit of a bore to actually work with it right now, because you have to always “pad” bank 0 to 32k and concatinate both bin files. For the time being – if you use it – you probably should write a script for that.
But sooner or later I will (most likely) support this in Vide – and it should than be largely automated.


All this was done because I am thinking about getting Aklabeth II out of it’s beta stage.
To do that I think I may want to add:

  • more text (instruction/intro)
  • music
  • saving of current game (DS 2431)

… since the beta is already scratching the 32k mark… I think I will go for 64k … and this obviously “needs” bankswitching.

64kB PCB’s can now be found “everywhere” so this cart will still fit/and be available for every vectrex – and not “just” VecFevers or “PiTrexes”.

Cheers

Malban

Tagged on: , ,

4 thoughts on “5th of July: Visiting old Friends…

  1. Peer

    Hey, great to hear from you again!

    Don’t worry, I am not getting the creeps. I am grading papers and student projects right now, so I am seeing lots of creepy code, and I am very much used to it ;-)And thinking of it, when it comes to Vectrex C programming, in a sense you have to write creepy C code if you want your binary to run efficiently. But that is part of the fun, and that is why I enjoy Vectrex C programming so much. Writing nice and ordinary C code on modern system is so boring…

    There are some additional ways (tricks?) to force gcc6809 to map certain code or data to a specific memory address. Let’s get in touch 🙂

    Cheers,
    Peer

  2. Vectrexer

    ⬆️ Exactly the person who I thought might be interested in this post. Wonder if it will be an option to incorporate into the programming course? Not that they need more space. Just a new skill.

    In general I have enjoyed reading all Malban’s posts on various topics. This one included. Always informative and interesting. Thanks again for researching and posting.

  3. Graham Toal

    I’m looking forward to trying this out, when I get a chance. A few other things to do first but I’ll get to it eventually. Thanks for posting the details. I have a few games in mind where this will be useful. (In particular a couple of word games, which need a lot of data).

    1. Malban Post author

      The next Vide version already contains this “automatically” – if you want to try it, than give me a call, I’ll get a “pre”-version to you.

Leave a Reply to Graham Toal Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.