Skip to content

Commit

Permalink
Dosbox + Turbo C 2.01 support (#3627)
Browse files Browse the repository at this point in the history
  • Loading branch information
partouf committed May 12, 2022
1 parent faf6e9d commit 67714e5
Show file tree
Hide file tree
Showing 9 changed files with 433 additions and 5 deletions.
28 changes: 28 additions & 0 deletions docs/TurboC.md
@@ -0,0 +1,28 @@
# Running Turbo C

Instructions on how to run Turbo C using Dosbox on Linux.

## Prerequisites

To run the Turbo C compiler you will need:

- Dosbox - the easiest way to install is to use your OS's package manager, e.g. `sudo apt install dosbox`
- Turbo C installation - if you have the 3 installation disks, first install those to a single directory with dosbox
- You will need to setup a directory to function as the `C` drive with a `TC` directory inside.
- Note that it's assumed all files are in uppercase

## Configuration

In the `turboc.properties` file you can see an example on how to setup the compiler to work with Compiler Explorer.

Make sure the `.dosbox` path is correct, as well as the `.root` and `.exe`. The `.root` indicates the root of the `C`
drive, and the `.exe` points to the actual `TCC.EXE`

## More notes

Note that Turbo C is C only, so it belongs in your `c.local.properties`.

Also note that you will immediately get an error with the default example source code, because the compiler doesn't
support `//` comments.

Also note that in this old C version, you must declare all variables in the first few lines of your functions.
15 changes: 15 additions & 0 deletions docs/turboc.properties
@@ -0,0 +1,15 @@
compilers=turboc

group.turboc.groupName=Turbo C
group.turboc.baseName=Turbo C
group.turboc.compilers=turboc201
group.turboc.compilerType=turboc
group.turboc.isSemVer=true
group.turboc.demangler=
group.turboc.supportsBinary=false
group.turboc.supportsExecute=false

compiler.turboc201.semver=2.01
compiler.turboc201.exe=/home/user/tc201/TC/TCC.EXE
compiler.turboc201.root=/home/user/tc201
compiler.turboc201.dosbox=/usr/bin/dosbox
22 changes: 19 additions & 3 deletions lib/base-compiler.d.ts
Expand Up @@ -27,10 +27,26 @@ export declare class BaseCompiler {
public compiler;
public lang;
public outputFilebase: string;
protected mtime;
protected env;
protected compileFilename;
protected asm;
protected getSharedLibraryPathsAsArguments(libraries: object[], libDownloadPath: string);
protected getSharedLibraryPathsAsLdLibraryPaths(libraries: object[]);
protected getCompilerCacheKey(compiler: string, args: string[], options: object);
protected async execCompilerCached(compiler, args, options);
protected async newTempDir();
protected filename(fn: string): string;
protected optionsForFilter(filters: object, outputFilename: string): string[];
protected getExtraFilepath(dirPath: string, filename: string): string;
protected async writeAllFiles(dirPath: string, source: string, files: any[], filters: object);
protected async writeMultipleFiles(files: any[], dirPath: string): Promise<any[]>;
public compilerProps: (key: string) => string;
public getOutputFilename(path: string, filename: string): string;
public exec(filepath: string, args: string[], execOptions);
public getOutputFilename(dirPath: string, outputFilebase: string, key?: object): string;
public async exec(filepath: string, args: string[], execOptions);
public parseCompilationOutput(result, filename: string);
public getDefaultExecOptions();
public runCompiler(compiler: string, args: string[], filename: string, execOptions);
public async runCompiler(compiler: string, args: string[], filename: string, execOptions);
public async getVersion();
protected getArgumentParser(): class;
}
1 change: 1 addition & 0 deletions lib/compilers/_all.js
Expand Up @@ -87,3 +87,4 @@ export {WslVcCompiler} from './wsl-vc';
export {ZigCompiler} from './zig';
export {ZigCC} from './zigcc';
export {ZigCXX} from './zigcxx';
export {TurboCCompiler} from './turboc';
7 changes: 7 additions & 0 deletions lib/compilers/argument-parsers.js
Expand Up @@ -351,3 +351,10 @@ export class TypeScriptNativeParser extends BaseParser {
return compiler;
}
}

export class TurboCParser extends BaseParser {
static async parse(compiler) {
await TurboCParser.getOptions(compiler, '');
return compiler;
}
}
168 changes: 168 additions & 0 deletions lib/compilers/dosbox-compiler.ts
@@ -0,0 +1,168 @@
// Copyright (c) 2022, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

import path from 'path';

import fs from 'fs-extra';

import {BaseCompiler} from '../base-compiler';
import * as exec from '../exec';
import {logger} from '../logger';
import {TurboCAsmParser} from '../parsers/asm-parser-turboc';

export class DosboxCompiler extends BaseCompiler {
private readonly dosbox: string;
private readonly root: string;

constructor(compilerInfo, env) {
super(compilerInfo, env);

this.dosbox = this.compilerProps(`compiler.${this.compiler.id}.dosbox`);
this.root = this.compilerProps(`compiler.${this.compiler.id}.root`);
this.asm = new TurboCAsmParser(this.compilerProps);
}

protected override async writeMultipleFiles(files: any[], dirPath: string): Promise<any[]> {
const filesToWrite: any[] = [];

for (const file of files) {
if (!file.filename) throw new Error('One of more files do not have a filename');

const fullpath = this.getExtraFilepath(dirPath, file.filename);
const contents = file.contents.replaceAll(/\n/g, '\r\n');
filesToWrite.push(fs.outputFile(fullpath, contents));
}

return Promise.all(filesToWrite);
}

protected override async writeAllFiles(dirPath: string, source: string, files: any[], filters: object) {
if (!source) throw new Error(`File ${this.compileFilename} has no content or file is missing`);

const inputFilename = path.join(dirPath, this.compileFilename);
await fs.writeFile(inputFilename, source.replaceAll(/\n/g, '\r\n'));

if (files && files.length > 0) {
(filters as any).dontMaskFilenames = true;

await this.writeMultipleFiles(files, dirPath);
}

return {
inputFilename,
};
}

private getDosboxArgs(tempDir: string, compileArgs: string[]) {
const binPath = path.relative(this.root, path.dirname(this.compiler.exe));
const exeName = path.basename(this.compiler.exe).replace(/\.exe$/i, '');
return [
'-c',
`mount c ${this.root}`,
'-c',
`mount d ${tempDir}`,
'-c',
`PATH=%PATH%;C:\\${binPath}`,
'-c',
'd:',
'-c',
`${exeName} ${compileArgs.join(' ')} > STDOUT.TXT`,
'-c',
'exit',
];
}

private getDosboxEnv() {
return {
SDL_VIDEODRIVER: 'dummy',
};
}

protected override async execCompilerCached(compiler, args, options) {
if (this.mtime === null) {
throw new Error('Attempt to access cached compiler before initialise() called');
}
if (!options) {
options = this.getDefaultExecOptions();
options.timeoutMs = 0;
options.ldPath = this.getSharedLibraryPathsAsLdLibraryPaths([]);
}

const key = this.getCompilerCacheKey(compiler, args, options);
let result = await this.env.compilerCacheGet(key);
if (!result) {
result = await this.env.enqueue(async () => this.exec(compiler, args, options));
if (result.okToCache) {
this.env
.compilerCachePut(key, result)
.then(() => {
// Do nothing, but we don't await here.
})
.catch(e => {
logger.info('Uncaught exception caching compilation results', e);
});
}
}

return result;
}

public override async exec(filepath: string, args: string[], execOptions: any) {
if (!execOptions) {
execOptions = this.getDefaultExecOptions();
}

execOptions.env = this.getDosboxEnv();

if (!execOptions.customCwd) {
execOptions.customCwd = await this.newTempDir();
}

const tempDir = execOptions.customCwd;
const fullArgs = this.getDosboxArgs(tempDir, args);

const result = await exec.executeDirect(this.dosbox, fullArgs, execOptions);

const stdoutFilename = path.join(tempDir, 'STDOUT.TXT');
const stdout = await fs.readFile(stdoutFilename);
(result as any).stdout = stdout.toString('utf8');

return result;
}

public override async runCompiler(compiler, options, inputFilename, execOptions) {
return super.runCompiler(
compiler,
options.map(option => {
if (option === inputFilename) {
return path.basename(option);
} else {
return option;
}
}),
inputFilename,
execOptions,
);
}
}
3 changes: 1 addition & 2 deletions lib/compilers/dotnet.ts
Expand Up @@ -38,7 +38,6 @@ class DotNetCompiler extends BaseCompiler {
private clrBuildDir: string;
private additionalSources: string;
private langVersion: string;
protected asm: DotNetAsmParser;

constructor(compilerInfo, env) {
super(compilerInfo, env);
Expand Down Expand Up @@ -174,7 +173,7 @@ class DotNetCompiler extends BaseCompiler {
return compilerResult;
}

optionsForFilter() {
override optionsForFilter() {
return this.compilerOptions;
}

Expand Down
75 changes: 75 additions & 0 deletions lib/compilers/turboc.ts
@@ -0,0 +1,75 @@
// Copyright (c) 2022, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

import path from 'path';

import {logger} from '../logger';

import {TurboCParser} from './argument-parsers';
import {DosboxCompiler} from './dosbox-compiler';

export class TurboCCompiler extends DosboxCompiler {
static get key() {
return 'turboc';
}

override optionsForFilter() {
return ['-B'];
}

override getSharedLibraryPathsAsArguments(libraries: object[], libDownloadPath: string) {
return [];
}

override getSharedLibraryPathsAsLdLibraryPaths(libraries: object[]) {
return [];
}

override async getVersion() {
logger.info(`Gathering ${this.compiler.id} version information on ${this.compiler.exe}...`);
if (this.compiler.explicitVersion) {
logger.debug(`${this.compiler.id} has forced version output: ${this.compiler.explicitVersion}`);
return {stdout: [this.compiler.explicitVersion], stderr: [], code: 0};
}
const execOptions = this.getDefaultExecOptions();
const versionFlag = '';
execOptions.timeoutMs = 0;
execOptions.ldPath = this.getSharedLibraryPathsAsLdLibraryPaths([]);

try {
return this.execCompilerCached(this.compiler.exe, [versionFlag], execOptions);
} catch (err) {
logger.error(`Unable to get version for compiler '${this.compiler.exe}' - ${err}`);
return null;
}
}

override getOutputFilename(dirPath: string, outputFilebase: string, key?: object) {
return path.join(dirPath, 'EXAMPLE.ASM');
}

override getArgumentParser() {
return TurboCParser;
}
}

0 comments on commit 67714e5

Please sign in to comment.