Task 101: .COM File Format
Task 101: .COM File Format
The .COM file format, as utilized in MS-DOS and compatible systems such as CP/M, consists of a raw binary executable without any header or structured metadata. The file contains machine code and data that are loaded directly into memory at offset 0x100 within a single 64KB segment. There are no relocations, sections, or internal properties; execution begins immediately at the loaded address. The format imposes a maximum file size of approximately 65,280 bytes to fit within the segment after accounting for the 256-byte Program Segment Prefix (PSP).
The properties of the .COM file format intrinsic to its file system, based on the standard MS-DOS FAT directory entry structure (32 bytes per entry), are as follows. These represent the metadata stored in the file system for any file, including those with the .COM extension:
- Filename: 8 bytes, ASCII characters padded with spaces.
- Extension: 3 bytes, ASCII characters padded with spaces (typically "COM").
- Attributes: 1 byte, bitfield (bit 0: read-only; bit 1: hidden; bit 2: system; bit 3: volume label; bit 4: directory; bit 5: archive).
- Reserved: 1 byte (often used for VFAT case information in later systems).
- Creation time tenths: 1 byte (tenths of a second, range 0-199).
- Creation time: 2 bytes (hours: bits 11-15, minutes: bits 5-10, seconds/2: bits 0-4).
- Creation date: 2 bytes (year-1980: bits 9-15, month: bits 5-8, day: bits 0-4).
- Last access date: 2 bytes (same format as creation date).
- High starting cluster: 2 bytes (high 16 bits of starting cluster for FAT32; 0 for FAT12/16).
- Last modification time: 2 bytes (same format as creation time).
- Last modification date: 2 bytes (same format as creation date).
- Low starting cluster: 2 bytes (starting cluster number).
- File size: 4 bytes (size in bytes).
These properties are not embedded within the .COM file itself but are maintained by the file system (e.g., FAT) in the directory entry.
Two direct download links for .COM files are:
- https://raw.githubusercontent.com/microsoft/MS-DOS/main/v1.25/bin/DEBUG.COM (MS-DOS 1.25 DEBUG.COM).
- https://raw.githubusercontent.com/microsoft/MS-DOS/main/v2.0/bin/COMMAND.COM (MS-DOS 2.0 COMMAND.COM).
The following is an HTML page with embedded JavaScript that enables drag-and-drop functionality for a .COM file. Upon dropping the file, it displays the available properties (limited to those accessible via the browser's File API, such as name, size, and last modification time; full file system metadata like creation time or attributes requires server-side access and is not feasible in a client-side browser context). The code assumes the dropped file is a .COM file and validates the extension.
The following Python class opens a .COM file path, reads and decodes its file system properties using standard library functions (os and stat), prints them to the console, and supports writing (modifying) select properties such as modification time and read-only attribute. Note that some properties (e.g., creation time, specific DOS attributes) require platform-specific APIs (e.g., on Windows); this implementation focuses on cross-platform compatibility where possible.
import os
import stat
import time
import platform
class ComFileHandler:
def __init__(self, filepath):
if not filepath.lower().endswith('.com'):
raise ValueError("File must have .COM extension")
self.filepath = filepath
self.filename = os.path.basename(filepath)
self.stats = os.stat(filepath)
def read_properties(self):
properties = {
'Filename': self.filename.rsplit('.', 1)[0],
'Extension': 'COM',
'Attributes': self._get_attributes(),
'Reserved': 'N/A (not directly accessible)',
'Creation time tenths': 'N/A (platform-dependent)',
'Creation time': time.strftime('%H:%M:%S', time.localtime(self.stats.st_ctime)) if platform.system() == 'Windows' else 'N/A',
'Creation date': time.strftime('%Y-%m-%d', time.localtime(self.stats.st_ctime)) if platform.system() == 'Windows' else 'N/A',
'Last access date': time.strftime('%Y-%m-%d', time.localtime(self.stats.st_atime)),
'High starting cluster': 'N/A (requires low-level FS access)',
'Last modification time': time.strftime('%H:%M:%S', time.localtime(self.stats.st_mtime)),
'Last modification date': time.strftime('%Y-%m-%d', time.localtime(self.stats.st_mtime)),
'Low starting cluster': 'N/A (requires low-level FS access)',
'File size': f"{self.stats.st_size} bytes"
}
return properties
def _get_attributes(self):
mode = self.stats.st_mode
attrs = []
if mode & stat.S_IRUSR == 0: attrs.append('Read-only')
if platform.system() == 'Windows':
# Windows-specific for hidden/system
import win32api, win32con
win_attrs = win32api.GetFileAttributes(self.filepath)
if win_attrs & win32con.FILE_ATTRIBUTE_HIDDEN: attrs.append('Hidden')
if win_attrs & win32con.FILE_ATTRIBUTE_SYSTEM: attrs.append('System')
if win_attrs & win32con.FILE_ATTRIBUTE_ARCHIVE: attrs.append('Archive')
return ', '.join(attrs) or 'None'
def print_properties(self):
props = self.read_properties()
for key, value in props.items():
print(f"{key}: {value}")
def write_property(self, prop, value):
if prop == 'Last modification time':
mod_time = time.mktime(time.strptime(value, '%Y-%m-%d %H:%M:%S'))
os.utime(self.filepath, (self.stats.st_atime, mod_time))
elif prop == 'Read-only':
mode = self.stats.st_mode
os.chmod(self.filepath, mode & ~stat.S_IWRITE if value else mode | stat.S_IWRITE)
else:
raise ValueError("Only select properties can be written")
# Example usage:
# handler = ComFileHandler('path/to/file.com')
# handler.print_properties()
# handler.write_property('Last modification time', '2025-09-11 12:00:00')
The following Java class opens a .COM file path, reads and decodes its file system properties using java.nio.file, prints them to the console, and supports writing (modifying) select properties such as modification time and read-only attribute. Some properties require low-level access and are noted as N/A.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.DosFileAttributes;
import java.nio.file.attribute.FileTime;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ComFileHandler {
private final Path path;
private final BasicFileAttributes attrs;
public ComFileHandler(String filepath) throws IOException {
this.path = Paths.get(filepath);
if (!path.toString().toLowerCase().endsWith(".com")) {
throw new IllegalArgumentException("File must have .COM extension");
}
this.attrs = Files.readAttributes(path, BasicFileAttributes.class);
}
public void readAndPrintProperties() throws IOException {
DosFileAttributes dosAttrs = Files.readAttributes(path, DosFileAttributes.class);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("Filename: " + path.getFileName().toString().split("\\.")[0]);
System.out.println("Extension: COM");
System.out.println("Attributes: " + (dosAttrs.isReadOnly() ? "Read-only, " : "") +
(dosAttrs.isHidden() ? "Hidden, " : "") +
(dosAttrs.isSystem() ? "System, " : "") +
(dosAttrs.isArchive() ? "Archive" : ""));
System.out.println("Reserved: N/A");
System.out.println("Creation time tenths: N/A");
System.out.println("Creation time: " + sdf.format(new Date(attrs.creationTime().toMillis())));
System.out.println("Creation date: " + sdf.format(new Date(attrs.creationTime().toMillis())).split(" ")[0]);
System.out.println("Last access date: " + sdf.format(new Date(attrs.lastAccessTime().toMillis())).split(" ")[0]);
System.out.println("High starting cluster: N/A");
System.out.println("Last modification time: " + sdf.format(new Date(attrs.lastModifiedTime().toMillis())));
System.out.println("Last modification date: " + sdf.format(new Date(attrs.lastModifiedTime().toMillis())).split(" ")[0]);
System.out.println("Low starting cluster: N/A");
System.out.println("File size: " + attrs.size() + " bytes");
}
public void writeProperty(String prop, String value) throws IOException {
if (prop.equals("Last modification time")) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date date = sdf.parse(value);
Files.setLastModifiedTime(path, FileTime.fromMillis(date.getTime()));
} catch (Exception e) {
throw new IOException("Invalid date format");
}
} else if (prop.equals("Read-only")) {
DosFileAttributes dosAttrs = Files.readAttributes(path, DosFileAttributes.class);
Files.setAttribute(path, "dos:readonly", Boolean.parseBoolean(value));
} else {
throw new IllegalArgumentException("Only select properties can be written");
}
}
// Example usage:
// public static void main(String[] args) throws IOException {
// ComFileHandler handler = new ComFileHandler("path/to/file.com");
// handler.readAndPrintProperties();
// handler.writeProperty("Last modification time", "2025-09-11 12:00:00");
// }
}
The following JavaScript class (intended for Node.js environment) opens a .COM file path, reads and decodes its file system properties using fs module, prints them to the console, and supports writing (modifying) select properties. Run with Node.js (e.g., node script.js).
const fs = require('fs');
const pathModule = require('path');
class ComFileHandler {
constructor(filepath) {
if (!filepath.toLowerCase().endsWith('.com')) {
throw new Error('File must have .COM extension');
}
this.filepath = filepath;
this.stats = fs.statSync(filepath);
}
readProperties() {
const dateFormat = (date) => date.toISOString();
return {
'Filename': pathModule.basename(this.filepath).split('.').slice(0, -1).join('.'),
'Extension': 'COM',
'Attributes': this._getAttributes(),
'Reserved': 'N/A',
'Creation time tenths': 'N/A',
'Creation time': dateFormat(new Date(this.stats.birthtime)).split('T')[1].slice(0, 8),
'Creation date': dateFormat(new Date(this.stats.birthtime)).split('T')[0],
'Last access date': dateFormat(new Date(this.stats.atime)).split('T')[0],
'High starting cluster': 'N/A',
'Last modification time': dateFormat(new Date(this.stats.mtime)).split('T')[1].slice(0, 8),
'Last modification date': dateFormat(new Date(this.stats.mtime)).split('T')[0],
'Low starting cluster': 'N/A',
'File size': `${this.stats.size} bytes`
};
}
_getAttributes() {
const mode = this.stats.mode;
let attrs = [];
if ((mode & 0o200) === 0) attrs.push('Read-only'); // Approximate
// Note: Full DOS attributes require OS-specific extensions
return attrs.join(', ') || 'None';
}
printProperties() {
const props = this.readProperties();
for (const [key, value] of Object.entries(props)) {
console.log(`${key}: ${value}`);
}
}
writeProperty(prop, value) {
if (prop === 'Last modification time') {
const modTime = new Date(value).getTime() / 1000;
fs.utimesSync(this.filepath, this.stats.atimeMs / 1000, modTime);
} else if (prop === 'Read-only') {
let mode = this.stats.mode;
fs.chmodSync(this.filepath, value ? mode & ~0o222 : mode | 0o222);
} else {
throw new Error('Only select properties can be written');
}
}
}
// Example usage:
// const handler = new ComFileHandler('path/to/file.com');
// handler.printProperties();
// handler.writeProperty('Last modification time', '2025-09-11T12:00:00');
The following C class (struct with functions) opens a .COM file path, reads and decodes its file system properties using stat and time functions, prints them to the console, and supports writing (modifying) select properties. Compile with a standard C compiler (e.g., gcc file.c -o program).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <utime.h>
#ifdef _WIN32
#include <windows.h>
#endif
typedef struct {
char* filepath;
struct stat stats;
} ComFileHandler;
void init_ComFileHandler(ComFileHandler* handler, const char* filepath) {
if (strstr(filepath, ".com") == NULL && strstr(filepath, ".COM") == NULL) {
fprintf(stderr, "File must have .COM extension\n");
exit(1);
}
handler->filepath = strdup(filepath);
if (stat(filepath, &handler->stats) != 0) {
perror("stat");
exit(1);
}
}
void print_properties(ComFileHandler* handler) {
char* filename = strrchr(handler->filepath, '/');
filename = filename ? filename + 1 : handler->filepath;
char* ext = strrchr(filename, '.');
char name[256];
strncpy(name, filename, ext - filename);
name[ext - filename] = '\0';
struct tm* tm;
char buf[32];
printf("Filename: %s\n", name);
printf("Extension: COM\n");
#ifdef _WIN32
DWORD attrs = GetFileAttributes(handler->filepath);
printf("Attributes: ");
if (attrs & FILE_ATTRIBUTE_READONLY) printf("Read-only ");
if (attrs & FILE_ATTRIBUTE_HIDDEN) printf("Hidden ");
if (attrs & FILE_ATTRIBUTE_SYSTEM) printf("System ");
if (attrs & FILE_ATTRIBUTE_ARCHIVE) printf("Archive");
printf("\n");
#else
printf("Attributes: %s\n", (handler->stats.st_mode & S_IWUSR) == 0 ? "Read-only" : "None");
#endif
printf("Reserved: N/A\n");
printf("Creation time tenths: N/A\n");
tm = localtime(&handler->stats.st_ctime);
strftime(buf, sizeof(buf), "%H:%M:%S", tm);
printf("Creation time: %s\n", buf);
strftime(buf, sizeof(buf), "%Y-%m-%d", tm);
printf("Creation date: %s\n", buf);
tm = localtime(&handler->stats.st_atime);
strftime(buf, sizeof(buf), "%Y-%m-%d", tm);
printf("Last access date: %s\n", buf);
printf("High starting cluster: N/A\n");
tm = localtime(&handler->stats.st_mtime);
strftime(buf, sizeof(buf), "%H:%M:%S", tm);
printf("Last modification time: %s\n", buf);
strftime(buf, sizeof(buf), "%Y-%m-%d", tm);
printf("Last modification date: %s\n", buf);
printf("Low starting cluster: N/A\n");
printf("File size: %ld bytes\n", (long)handler->stats.st_size);
}
void write_property(ComFileHandler* handler, const char* prop, const char* value) {
if (strcmp(prop, "Last modification time") == 0) {
struct tm tm;
strptime(value, "%Y-%m-%d %H:%M:%S", &tm);
time_t mod_time = mktime(&tm);
struct utimbuf ut;
ut.actime = handler->stats.st_atime;
ut.modtime = mod_time;
utime(handler->filepath, &ut);
} else if (strcmp(prop, "Read-only") == 0) {
mode_t mode = handler->stats.st_mode;
chmod(handler->filepath, atoi(value) ? mode & ~0222 : mode | 0222);
} else {
fprintf(stderr, "Only select properties can be written\n");
}
}
void free_ComFileHandler(ComFileHandler* handler) {
free(handler->filepath);
}
// Example usage:
// int main() {
// ComFileHandler handler;
// init_ComFileHandler(&handler, "path/to/file.com");
// print_properties(&handler);
// write_property(&handler, "Last modification time", "2025-09-11 12:00:00");
// free_ComFileHandler(&handler);
// return 0;
// }