Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
74181b0
add start of files for shared resources API #70
Jun 13, 2025
9857ede
get shres code building with cc65 #70
Jun 13, 2025
85d82cc
fix typo in symbol name #70
Jun 13, 2025
7e1b1f3
Implement shdopen() #70
Jun 13, 2025
3fac544
more work on SHRES API #70
Jun 13, 2025
05da1b5
work on implementing shread() and shopen() #70
Jun 14, 2025
485f8f9
fix shread() sector offset. Remove debug output #70
Jun 14, 2025
7755d42
get shopen() file search working etc #70
Jun 14, 2025
3672bb4
remove debug output #70
Jun 14, 2025
24e2695
implement shseek() #70
Jun 14, 2025
6eb8edf
run clang-format on shres files #70
Jun 14, 2025
0ee08bb
add doxygen documentation #70
Jun 14, 2025
7d954db
reapply clang-format #70
Jun 14, 2025
5f70804
initial llvm-mos implementation of shres_asm.s #70
Jun 14, 2025
b6d1c7b
add shres API to llvm cmake #70
Jun 14, 2025
95cd1a9
wait for sector read to complete in shread() #70
Jun 16, 2025
61ab9ea
add argument to chdirroot() function #73
Aug 29, 2025
d987fb8
ensure SD card buffer selected in shread #70
Aug 30, 2025
8174c8f
remove vestical openfile call in chdir #74
Aug 30, 2025
f230e1a
fix llvm shres issue #70
Sep 25, 2025
33eb3ba
fix llvm -Oz bug with ldq/stq #70
Sep 25, 2025
6a44500
fix llvm target bugs #70
Sep 25, 2025
78931f4
usleep() bug with values not modulo 64usec #70
Sep 27, 2025
9d8888e
fix Y Z pop order #70
Sep 27, 2025
031c0e0
fix llvm behaviour for shres #70
Sep 27, 2025
141dd62
fix chdirroot for llvm #70
Sep 28, 2025
8433fff
preserve Y value
Sep 28, 2025
5c2d089
debug shres stuff #70
Sep 28, 2025
106c10d
Reduce memory requirements for shres #76
Jan 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/mega65/fileio.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ chdir(char* filename);
__attribute__((leaf))
#endif
uint8_t
chdirroot(void);
chdirroot(uint8_t partition);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update tests/test-fileio.c to reflect this breaking change. CI currently fails.


/**
* @brief Struct for holding version information of the hypervisor
Expand Down
136 changes: 136 additions & 0 deletions include/mega65/shres.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/**
* @file shres.h
* @brief MEGA65 SYSPART Shared Resources Access API
*
* This API provides access to shared resources stored in the MEGA65 system
* partition (SYSPART), including files such as fonts, icons, or other binary
* assets.
*
* Resources are accessed via a special trap instruction and a sector-based
* directory format. This API allows enumeration, opening, reading, and seeking
* within those shared resources.
*/

#ifndef SHRES_H
#define SHRES_H

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allow use from C++ by encapsulating with:

#ifdef __cplusplus
// Being compiled by a C++ compiler, inhibit name mangling
extern "C" {
#endif

...

#ifdef __cplusplus
} // End of extern "C"
#endif

/// Triggers the low-level SYSPART shared resource trap.
extern void shres_trap(void);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add

#ifdef __clang__
__attribute__((leaf))
#endif
in front of function.


/// Registers used for communicating with the shared resource trap.
extern unsigned char shres_regs[5];
Copy link
Collaborator

@mlund mlund Jun 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be a global variable? With llvm-mos, taking it as an argument for shres_trap() is usually better as the compiler has more optimization opportunities, e.g. use registers and reduced memory. I would prefer shres_trap() to use inline asm with llvm-mos, again for possible compiler optimization of arguments and return values (if any). I'm a little rusty but would be happy to look into this after the merge. It would be helpful with a test file in tests/ for that purpose.


/// Flag indicating that the resource is a font.
#define SHRES_FLAG_FONT 1

/// Indicates the font is 16x16 pixels (requires SHRES_FLAG_FONT).
#define SHRES_FLAG_16x16 2

/// Indicates the font supports Unicode (requires SHRES_FLAG_FONT).
#define SHRES_FLAG_UNICODE 4

/// Maximum length of a resource name (not including null terminator).
#define MAX_RES_NAME_LEN 240

/**
* @struct shared_resource
* @brief Represents a file or other data resource in the SYSPART directory.
*
* This structure includes metadata stored on disk (such as name, flags, and
* sector info), as well as an in-memory field to track read position for
* streaming access.
*/
struct shared_resource_dirent {
/// First sector of the resource.
unsigned long first_sector;

/// Length of the resource in sectors.
unsigned long length_in_sectors;

/// Length of the resource in bytes.
unsigned long length;

/// Bitmask of resource flags (e.g., SHRES_FLAG_FONT, SHRES_FLAG_16x16).
unsigned long flags;

/// Length of the resource name (as stored in the directory).
unsigned char name_len;

/// Null-terminated name of the resource (up to MAX_RES_NAME_LEN).
char name[MAX_RES_NAME_LEN + 1];
};

struct shared_resource {
/// First sector of the resource.
unsigned long first_sector;

/// Length of the resource in sectors.
unsigned long length_in_sectors;

/// Length of the resource in bytes.
unsigned long length;

/// Current read position in bytes (used internally; not stored on disk).
unsigned long position;
};


/// Type alias for a directory handle (really a sector index).
#define shared_resource_dir unsigned int

/**
* @brief Opens a named shared resource and verifies required flags.
*
* @param resource_name The null-terminated name of the resource to open.
* @param required_flags A bitmask of flags that must be set on the resource.
* @param file_handle Pointer to a shared_resource structure to populate.
* @return 0 on success, 1 if not found or error.
*/
char shopen(char* resource_name, unsigned long required_flags,
Copy link
Collaborator

@mlund mlund Jun 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

char -> bool by including <stdbool.h> (should be available on both cc65 and clang)

struct shared_resource* file_handle);

/**
* @brief Reads data from an open shared resource file.
*
* @param ptr Pointer to the output buffer.
* @param count Number of bytes to read.
* @param f Pointer to the shared_resource handle.
* @return Number of bytes actually read.
*/
unsigned int shread(
unsigned char* ptr, unsigned int count, struct shared_resource* f);

/**
* @brief Seeks to a new position in an open shared resource.
*
* @param f Pointer to the shared_resource handle.
* @param offset Byte offset from the origin.
* @param whence One of SEEK_SET, SEEK_CUR, or SEEK_END.
* @return 0 on success, 1 if the seek was out of bounds or invalid.
*/
char shseek(struct shared_resource* f, long offset, unsigned char whence);

/**
* @brief Opens the shared resource directory for iteration.
*
* @return A directory handle (starting sector index), or 0xFFFF on failure.
*/
shared_resource_dir shdopen(void);

/**
* @brief Reads the next matching directory entry.
*
* @param required_flags A bitmask of required flags the resource must match.
* @param directory_handle Pointer to the directory handle returned by
* shdopen().
* @param dirent Pointer to a shared_resource structure to populate.
* @return
* - 0 on success (entry read and matches),
* - 1 if the trap failed,
* - 2 if end of directory reached (no more entries match).
*/
char shdread(unsigned long required_flags,
shared_resource_dir* directory_handle,
struct shared_resource_dirent* dirent);

#endif // SHRES_H
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
set(assembler
llvm/fileio.s
llvm/dirent.s
llvm/shres_asm.s
llvm/memory_asm.s)

set(objects
Expand All @@ -14,6 +15,7 @@ set(objects
mouse.c
random.c
sdcard.c
shres.c
targets.c
tests.c
time.c)
Expand All @@ -30,6 +32,7 @@ set(headers
${PROJECT_SOURCE_DIR}/include/mega65/mouse.h
${PROJECT_SOURCE_DIR}/include/mega65/random.h
${PROJECT_SOURCE_DIR}/include/mega65/sdcard.h
${PROJECT_SOURCE_DIR}/include/mega65/shres.h
${PROJECT_SOURCE_DIR}/include/mega65/targets.h
${PROJECT_SOURCE_DIR}/include/mega65/tests.h
${PROJECT_SOURCE_DIR}/include/mega65/time.h)
Expand Down
8 changes: 5 additions & 3 deletions src/cc65/fileio.s
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,15 @@ _close:
rts

_chdirroot:
;; HYPPO requires partition number in X.
tax
;; Change to root directory of volume
lda #$3C
sta $d640
clv
;; HYPPO trap_dos_cdroot() doesn't set return value: It is deemed to always succeed
;; if called on a valid disk.
ldx #$00
ldx #$00
rts

Expand Down Expand Up @@ -177,9 +182,6 @@ chdir_file_exists:
lda #$0C
sta $d640
clv
lda #$18
sta $D640
clv
ldx #$00
rts

Expand Down
28 changes: 28 additions & 0 deletions src/cc65/shres_asm.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

.setcpu "65C02"
.export _shres_trap,_shres_regs

.SEGMENT "CODE"

.p4510

;; closedir takes file descriptor as argument (appears in A)
_shres_trap:
NEG ; Prefix instructions to make LDA -> LDQ
NEG
LDA _shres_regs
STA $D645
NOP
NEG ; Prefix instructions to make STA -> STQ
NEG
STA _shres_regs
PHP
PLA
STA _shres_regs+4
LDX #$00
TAX
RTS

_shres_regs:
.dword 0 ; regs
.byte 0 ; processor flags
18 changes: 15 additions & 3 deletions src/hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,23 @@ void usleep(uint32_t micros)
// Each VIC-II raster line is ~64 microseconds
// this is not totally accurate, but is a reasonable approach
while (micros > 64) {
uint8_t b = PEEK(0xD012);
#ifdef LLVM_503_WORKAROUND
asm volatile(
"ldx $D012\n"
"1:\n"
"cpx $D012\n"
"beq 1b\n"
:
:
: "x" // X is clobbered
);
#else
uint8_t b = PEEK(0xD012);
while (PEEK(0xD012) == b) {
continue;
continue;
}
micros -= 64;
#endif
micros -= 64;
}
return;
}
15 changes: 9 additions & 6 deletions src/llvm/fileio.s
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ close:
.global chdirroot
.section .text.fileio_chdirroot,"ax",@progbits
chdirroot:
ldx #0
hyppo HYPPO_CDROOTDIR
rts

Expand All @@ -144,16 +145,18 @@ chdir:
hyppo HYPPO_FINDFILE
bcs chdir_ok
lda #FILE_ERROR
rts
rts
chdir_ok:
hyppo HYPPO_CHDIR
hyppo HYPPO_OPENFILE; outputs to A

rts

.global gethyppoversion
.section .text.fileio_gethyppoversion,"ax",@progbits
gethyppoversion:
hyppo HYPPO_GETVERSION ; outputs to Q = A, X, Y, Z
stq (__rc2) ; store Q to __rc2 pointer
ldz #0 ; Z must be cleared before returning
rts
phy
hyppo HYPPO_GETVERSION ; outputs to Q = A, X, Y, Z
stq (__rc2) ; store Q to __rc2 pointer
ldz #0 ; Z must be cleared before returning
ply
rts
49 changes: 49 additions & 0 deletions src/llvm/shres_asm.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.global _shres_trap
.global _shres_regs

.section .text
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to .section .text.shres_trap,"ax",@progbits


; _shres_trap()
; Sends a 32-bit argument in _shres_regs[0..3] to $D645,
; then stores 32-bit return result back into _shres_regs[0..3]
; and processor flags into _shres_regs[4].

_shres_trap:

phy
phz

lda _shres_regs+0
ldx _shres_regs+1
ldy _shres_regs+2
ldz _shres_regs+3

sta $D645 ; Store to the MEGA65 SYSPART trap address
nop ; Delay/stabilize (preserved from original)


; Store result back into _shres_regs
sta _shres_regs+0
stx _shres_regs+1
sty _shres_regs+2
stz _shres_regs+3

php
pla
sta _shres_regs+4 ; Save status register into 5th byte
ldx #0
txa

plz
ply

rts

; Register block: 4 bytes for argument/result, 1 byte for flags

.section .bss
_shres_regs:
.space 5


.section .text
Loading
Loading