diff --git a/libsrc/apple2/dir.inc b/libsrc/apple2/dir.inc
new file mode 100644
index 000000000..afad44eb7
--- /dev/null
+++ b/libsrc/apple2/dir.inc
@@ -0,0 +1,15 @@
+.struct DIR
+ FD .word
+ ENTRY_LENGTH .byte
+ ENTRIES_PER_BLOCK .byte
+ CURRENT_ENTRY .byte
+
+ .union
+ BYTES .byte 512
+ .struct CONTENT
+ PREV_BLOCK .word
+ NEXT_BLOCK .word
+ ENTRIES .byte
+ .endstruct
+ .endunion
+.endstruct
diff --git a/libsrc/apple2/opendir.c b/libsrc/apple2/opendir.c
deleted file mode 100644
index 1144d8511..000000000
--- a/libsrc/apple2/opendir.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*****************************************************************************/
-/* */
-/* opendir.h */
-/* */
-/* Open a directory */
-/* */
-/* */
-/* */
-/* (C) 2005 Oliver Schmidt,
*/
-/* */
-/* */
-/* This software is provided 'as-is', without any expressed or implied */
-/* warranty. In no event will the authors be held liable for any damages */
-/* arising from the use of this software. */
-/* */
-/* Permission is granted to anyone to use this software for any purpose, */
-/* including commercial applications, and to alter it and redistribute it */
-/* freely, subject to the following restrictions: */
-/* */
-/* 1. The origin of this software must not be misrepresented; you must not */
-/* claim that you wrote the original software. If you use this software */
-/* in a product, an acknowledgment in the product documentation would be */
-/* appreciated but is not required. */
-/* 2. Altered source versions must be plainly marked as such, and must not */
-/* be misrepresented as being the original software. */
-/* 3. This notice may not be removed or altered from any source */
-/* distribution. */
-/* */
-/*****************************************************************************/
-
-
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "dir.h"
-
-
-
-/*****************************************************************************/
-/* Data */
-/*****************************************************************************/
-
-
-
-extern char _cwd[FILENAME_MAX];
-
-
-
-/*****************************************************************************/
-/* Code */
-/*****************************************************************************/
-
-
-
-DIR* __fastcall__ opendir (register const char* name)
-{
- register DIR* dir;
-
- /* Alloc DIR */
- if ((dir = malloc (sizeof (*dir))) == NULL) {
-
- /* May not have been done by malloc() */
- _directerrno (ENOMEM);
-
- /* Return failure */
- return NULL;
- }
-
- /* Interpret dot as current working directory */
- if (*name == '.') {
- name = _cwd;
- }
-
- /* Open directory file */
- if ((dir->fd = open (name, O_RDONLY)) != -1) {
-
- /* Read directory key block */
- if (read (dir->fd,
- dir->block.bytes,
- sizeof (dir->block)) == sizeof (dir->block)) {
-
- /* Get directory entry infos from directory header */
- dir->entry_length = dir->block.bytes[0x23];
- dir->entries_per_block = dir->block.bytes[0x24];
-
- /* Skip directory header entry */
- dir->current_entry = 1;
-
- /* Return success */
- return dir;
- }
-
- /* EOF: Most probably no directory file at all */
- if (_oserror == 0) {
- _directerrno (EINVAL);
- }
-
- /* Cleanup directory file */
- close (dir->fd);
- }
-
- /* Cleanup DIR */
- free (dir);
-
- /* Return failure */
- return NULL;
-}
diff --git a/libsrc/apple2/opendir.s b/libsrc/apple2/opendir.s
new file mode 100644
index 000000000..b75b83636
--- /dev/null
+++ b/libsrc/apple2/opendir.s
@@ -0,0 +1,156 @@
+;
+; Colin Leroy-Mira , 2024
+;
+; DIR* __fastcall__ opendir (register const char* name)
+;
+
+ .export _opendir
+
+ .import _open, _read, _close
+ .import _malloc, _free
+ .import ___directerrno
+
+ .import ___oserror, __cwd
+
+ .import pushptr1, popptr1
+ .import pushax, pusha0
+
+ .importzp ptr1
+
+ .include "apple2.inc"
+ .include "dir.inc"
+ .include "errno.inc"
+ .include "fcntl.inc"
+ .include "zeropage.inc"
+
+.proc _opendir
+ sta ptr1
+ stx ptr1+1
+
+ ldy #$00
+ lda (ptr1),y
+ cmp #'.'
+ bne :+
+
+ lda #<__cwd
+ ldx #>__cwd
+ sta ptr1
+ stx ptr1+1
+
+: ; open directory
+ jsr pushptr1
+ lda #O_RDONLY
+ jsr pusha0
+
+ ldy #$04
+ jsr _open
+
+ cmp #$FF ; Did we succeed?
+ beq @return_null
+ pha ; Yes - Push fd for backup
+
+ ; malloc the dir struct
+ lda #<.sizeof(DIR)
+ ldx #>.sizeof(DIR)
+ jsr _malloc
+ bne :+
+
+ ; We failed to allocate
+ pla ; Get fd back
+ ldx #$00
+ jsr _close ; close it
+
+ lda #ENOMEM ; Set error
+ jsr ___directerrno
+
+@return_null:
+ lda #$00
+ tax
+ rts
+
+: ; Store dir struct to pointer
+ sta ptr1
+ stx ptr1+1
+
+ ; Push ptr1, read will destroy it
+ jsr pushptr1
+
+ ; Save fd to dir struct
+ lda #$00
+ ldy #DIR::FD + 1
+ sta (ptr1),y
+
+ dey
+ pla ; Get fd back
+ sta (ptr1),y
+
+ jsr pusha0 ; push fd for read
+ lda #DIR::BYTES
+ adc ptr1+1
+ tax
+ pla
+ jsr pushax ; Push dir->block.bytes for read
+
+ lda #<.sizeof(DIR::BYTES)
+ ldx #>.sizeof(DIR::BYTES)
+
+ jsr _read ; Read directory block
+ cpx #>.sizeof(DIR::BYTES)
+ bne @err_read
+ cmp #<.sizeof(DIR::BYTES)
+ beq @read_ok
+
+@err_read:
+ ; Read failed, exit
+ lda ___oserror
+ bne :+
+ lda #EINVAL
+ jsr ___directerrno
+
+: ; Close fd
+ jsr popptr1 ; Restore our dir pointer
+ ldy #$00
+ lda (ptr1),y ; Get fd
+ ldx #$00
+ jsr _close
+
+ ; Free dir structure
+ lda ptr1
+ ldx ptr1+1
+ jsr _free
+ jmp @return_null
+
+@read_ok:
+ ; Read succeeded, populate dir struct
+ jsr popptr1 ; Restore our dir pointer
+
+ ldy #$24 + DIR::BYTES
+ lda (ptr1),y ; ENTRIES_PER_BLOCK
+ pha ; Back it up
+
+ dey
+ lda (ptr1),y ; ENTRY_LENGTH
+
+ ldy #DIR::ENTRY_LENGTH
+ sta (ptr1),y
+
+ pla
+ .assert DIR::ENTRIES_PER_BLOCK = DIR::ENTRY_LENGTH + 1, error
+ iny
+ sta (ptr1),y
+
+ ; Skip directory header entry
+ .assert DIR::CURRENT_ENTRY = DIR::ENTRIES_PER_BLOCK + 1, error
+ iny
+ lda #$01
+ sta (ptr1),y
+
+ ; Return pointer to dir struct
+ lda ptr1
+ ldx ptr1+1
+ rts
+.endproc