Saturday, 14 June 2025

Accessing Shared Resources from the MEGA65 System Partition

In my previous post, I implemented a facility for accessing the brand new Shared Resources area of the MEGA65's System Partition.

This facility has been created to provide rapid access to content anywhere in large files --- initially unicode pre-rendered fonts for the MEGAphone.  Using a traditional file-system would require tracking file-system state, and traversing file-system structures.   This would add latency for time-critical operations, like retrieving individual character glyphs when rendering SMS messages.

This area works by implementing a simple extent-based file list in the system partition.  The shres and shresls tools exist for linux in the mega65-tools repository to populate and query the contents of this area on a MEGA65-formatted SD card.   

Only the latest development version of MEGA65 FDISK will format an SD card to include a shared resource area.

test_897.c in mega65-tools has unit tests for this functionality, including reporting if your HYPPO version is too old, or there doesn't seem to be a shared resources area defined in your system partition.

So now we need to tie all this together is some code that could eventually go in mega65-libc that provides a simple API for accessing it. I'm thinking something like:

char shopen(char *resource_name,unsigned long long required_flags, struct shared_resource *file_handle) -- open  

shread(unsigned char *ptr, unsigned int count, struct shared_resource *file_handle) -- Read a slab of bytes from the opened resource from the current offset.

shseek(struct shared_resource *,unsigned long long offset, unsigned char whence) -- Seek to arbitrary point in the file.

unsigned int shdopen() -- open directory handle to shared resource area

sddread(unsigned long long required_flags, unsigned int *directory_handle, struct shared_resource *dirent) -- read next directory entry that matches.  The resource is implicitly an opened resource (since "opening" is absurdly light weight, the way I'm doing it).

The shared_resource struct will simply contain the filename, flags, starting sector in the shared resource area, its length, and current seek position (initially 0):

#define MAX_RES_NAME_LEN 256
#define SEEK_SET 1
#define SEEK_CUR 2
#define SEEK_END 3 
struct shared_resource {
   char name[MAX_RES_NAME_LEN];
   unsigned long long flags;
   unsigned long long length;
   unsigned long long first_sector;
   unsigned long long position;
};
 

I'm tracking this via https://github.com/MEGA65/mega65-libc/issues/70.

To test this API, I'm just going to start writing a wrapper program in the megaphone-modular repository in src/telephony. 

Okay, so in the process of setting that up, I'm discovering that CC65 mainline doesn't have support for the 45GS02 instruction set, so I can't use LDQ and STQ directly.  I guess I'll just implement those as the underlying instruction sequences.

Next challenge is that the SD card keeps getting stuck busy.  One of the subtleties involved in this is that the SD card interface _does_ lockup if you ask it to read a sector while it's already reading a sector. We should probably fix that, but for now I have to live with it.

To deal with this, I've made the shared resource API functions check the SD card status, and fail with an error if it's busy. So it should never be calling a SHRES Hypervisor trap when the SD card is busy (remember that the SHRES Hypervisor trap does the sector read request, so this is vital).

char do_shres_trap(unsigned long arg)
{
  // Fail if SD card is busy
  if (PEEK(0xD680)&0x03) {
    printf("SD card is busy\n");
    return 1;
  }

  printf("SD card was idle\n");
  
  shres_regs[0] = (arg>>0)&0xff;
  shres_regs[1] = (arg>>8)&0xff;
  shres_regs[2] = (arg>>16)&0xff;
  shres_regs[3] = (arg>>24)&0xff;
  shres_trap();

  printf("Trap C=%d\n",shres_regs[4]&1);
  
  // Check trap response in P
  return (shres_regs[4]&0x01) ^0x01;

}

Basically the wrapper is the only way I call the trap, and it should reliably fail if the SD card interface is already busy.

So what's going wrong here?

Okay, so it looks like it's trying to read an invalid sector from the SD card for some reason now.  The call to shdopen() always asks for sector 0 of the shared resource area:

 

shared_resource_dir shdopen()
{
  char i;
  if (do_shres_trap(0)) return 0xffff;
 

So provided that shres_regs is getting set properly in the wrapper it calls, it should be working.

I _think_ there might be something funny where the HYPPO trap is not adding the SHRES start offset to the $D681-4 SD card registers properly.  

readsharedresourcetrap:
    cpq syspart_resources_area_size
    bcs bad_syspart_resource_sector_request
    ldq syspart_resources_area_start
    adcq $d681
    stq $d681
    ;; Ask SD card to read the sector.
    lda #$02
    sta $d680
    ;; Note that we don't wait for the request to finish -- that's
        ;; up to the end-user to do, so that we don't waste space here
    ;; in the hypervisor, where space is at an absolute premium.
        jmp return_from_trap_with_success

Ah!  There is indeed a logical error here. It should look like this:

readsharedresourcetrap:
    cpq syspart_resources_area_size
    bcs bad_syspart_resource_sector_request
    adcq syspart_resources_area_start
    stq $d681
    ;; Ask SD card to read the sector.
    lda #$02
    sta $d680
    ;; Note that we don't wait for the request to finish -- that's
        ;; up to the end-user to do, so that we don't waste space here
    ;; in the hypervisor, where space is at an absolute premium.
        jmp return_from_trap_with_success

 

Yay! With that fix, I can now iterate over the (slightly random) set of files I put in the shared resource area of my SD card:

So perhaps I'll next make it try to cat the README.md file to the screen, just as a proof-of-concept --- and make it use the API, forcing me to implement all of the API.

Here's the resulting program:

struct shared_resource dirent;
unsigned long required_flags = SHRES_FLAG_FONT | SHRES_FLAG_16x16 | SHRES_FLAG_UNICODE;

unsigned char buffer[128];

unsigned char i;

char filename_ascii[]={'r','e','a','d','m','e','.',0x6d,0x64,0};

void main(void)
{
  shared_resource_dir d;

  mega65_io_enable();

  // Make sure SD card is idle
  if (PEEK(0xD680)&0x03) {
    POKE(0xD680,0x00);
    POKE(0xD680,0x01);
    while(PEEK(0xD680)&0x3) continue;
    usleep(500000L);
  }

  if (shopen(filename_ascii,0,&dirent)) {
    printf("ERROR: Failed to open README.md\n");
    return;
  }

  {
    unsigned int b = 0;
    printf("Found text file\n");
    while(b = shread(buffer,128,&dirent)) {
      for(i=0;i<b;i++) {
    if (buffer[i]==0x0a) putchar(0x0d); else putchar(buffer[i]);
      }
      
    }
  }

  return;
}

And it works! And I can even use shseek() to print just a bit of the text file.

  {
    unsigned int b = 0;
    printf("Found README.md\n");
    shseek(&dirent,-200,SEEK_END);

    while(b = shread(buffer,128,&dirent)) {
      for(i=0;i<b;i++) {
    if (buffer[i]==0x0a) putchar(0x0d); else putchar(buffer[i]);
      }
      
    }
  }

 

Running this we can get something like this:


 In short, it works. So now it's time to make a pull-request into mega65-libc for this new API, and then we're done, and I can go back to working on making the actual unicode pre-rendered fonts for the MEGAphone telephony software! 

 

No comments:

Post a Comment