Skip to the content.

DOS file I/O, paths, 8.3 filenames, line endings

The things that surprise programmers who’ve only used Python on Unix or Windows.

8.3 filenames

DOS FAT filenames are 8 characters + a 3-character extension. The filesystem case-folds: Hello.PY and HELLO.py are the same file.

In Python, write your paths in uppercase to match what os.listdir() returns:

>>> os.listdir()
['MP.EXE', 'HELLO.PY', 'CACERTS.PEM']

You can open('hello.py') and it works — DOS case-folds at the filesystem level — but the os.path helpers don’t lowercase their results, so be consistent.

Long extensions get truncated silently

config.json saved to FAT becomes CONFIG.JSO. Plan around it: use .JSO or .CFG in your own conventions.

Path separators

DOS uses \ (backslash). In Python strings, that needs escaping:

path = 'C:\\PYLIB\\MYMODULE.PY'      # ok
path = r'C:\PYLIB\MYMODULE.PY'        # raw string — easier
path = '/c/pylib/MYMODULE.PY'         # FORWARD slash — also works

The third form works because DOS internally accepts both. You can mix freely. os.path.join uses \ on output:

>>> os.path.join('C:\\DOS', 'AUTOEXEC.BAT')
'C:\\DOS\\AUTOEXEC.BAT'

Drive letters

C:\ is a directory rooted on drive C. os.getcwd() returns something like 'A:\\DOS'; os.chdir('C:') switches drive without changing the C: cwd (DOS tracks one cwd per drive).

Line endings

DOS text files use CRLF (\r\n). MicroPython’s open(path, 'r') in this port returns the bytes as-is — there is no Universal Newlines translation. If you want lines without the \r:

with open('HELLO.TXT', 'r') as f:
    for line in f:
        line = line.rstrip('\r\n')
        print(repr(line))

For binary mode ('rb'), bytes come through unmodified — no translation at all.

INT 21h vs higher-level open

The Python open() is built on the port’s dos_int21_open (INT 21h AH=3D) → dos_int21_read (AH=3F) / dos_int21_write (AH=40) → dos_int21_close (AH=3E). The DPMI 0x0301 thunk routes the call into real mode. See dosint21 for raw access.

File modes supported

Mode Effect
'r' text read (no newline translation — see above)
'rb' binary read
'w' write text (truncate); currently maps to AH=3D R/W
'wb' write binary (truncate)
'a' append (currently maps to R/W; AH=0x6C extended open
  for true truncate/append is a TODO)

⚠️ 'w' on this port currently uses AH=3D in read-write mode plus manual truncation. For most use cases that’s fine; if you need guaranteed truncation semantics, use os.remove(path) first.

Sample sizes

See also