A compiler and framework for creating Atari 2600 games using C#. It uses the .NET Compiler Platform (Roslyn) to compile C# files, and Mono.Cecil to compile the resulting CIL into 6502 assembly.
Initially this project was written to compile C# straight to 6502 assembly. It has since been rewritten to compile C# to CIL, and then compile the CIL to 6502 assembly. The rewrite quickly surpassed the original implementation in both features and development speed. Development will continue towards the goal of porting my 2600 game to C#.
The current goal is to add all the features needed for me to port my attempt at a 2600 game to C#.
There's no collection of samples yet since they may quickly become obsolete. Below is an example of how you could, at the time of writing, write a program to cycle through the NTSC background colors. See the Features section below for a more complete list of features.
using static VCSFramework.Registers;
namespace Samples
{
public static class NtscBackgroundColorsSample
{
private static byte BackgroundColor; // Support for static fields.
public static void Main()
{
// Processor and memory initialization code is automatically injected by the compiler into
// the program's entry point, so there's no need to manually do it.
MainLoop:
// Perform vertical sync.
// This is the same logic that would be used in 6502 assembly as well.
VSync = 0b10; // TIA write-only registers implemented as setter-only properties.
WSync(); // TIA strobe registers implemented as methods.
WSync();
WSync();
Tim64T = 43;
VSync = 0;
// Actual logic to increment and set the background color every frame.
// The least significant bit is unused, so incrementing by 1 instead of 2 slows the flashing down.
BackgroundColor++;
ColuBk = BackgroundColor;
// Kill time until the vertical blank period is over.
while (InTim != 0) ; // PIA read-only registers implemented as getter-only properties.
WSync();
VBlank = 0;
// Visible image
byte lines = 191; // Local variable support.
while (lines != 0) // Support for while loops and some comparisons.
{
lines--;
WSync();
}
WSync();
VBlank = 0b10;
// Overscan
lines = 30;
while (lines != 0)
{
lines--;
WSync();
}
goto MainLoop; // goto support!
}
}
}
An incomplete list of supported features in no particular order.
- ⭕ Primitive Types
- ❌
bool
- ✔️
byte
- ❌
sbyte
- ❌
- ❌ Array types
- ❌ Pointer Types
- ⭕ Custom Types
- ✔️ Value Types
- ✔️ Single-byte types
- ✔️ Multi-byte types
- ✔️ Composite types (struct-in-struct)
- ⭕ Reference Types
- ✔️ Static reference types
- ❌ Instance reference types
- ✔️ Value Types
- ⭕ Static Members
- ✔️ Fields
- ❌ Properties
- ⭕ Methods
- ✔️ 0-parameter
- ✔️ >0-parameter
- ✔️
void
return - ❌ Non-
void
return
- ⭕ Inline Assembly
- ❌ Implied address mode inline assembly (
TXS
,SEI
, etc) - ✔️ Write-only
A
/X
/Y
registers
- ❌ Implied address mode inline assembly (
- ⭕ Optimizations
- ✔️ Redundant
PHA
/PLA
removal - ❌ Reuse memory addresses
- ✔️ Redundant
- ⭕ C#
- ✔️
goto
- ✔️
unsafe
- ✔️
default
- ✔️
- ⭕ CIL OpCodes
- ⭕ Arithmetic
- ✔️ Addition (
add
, no overflow check) - ✔️ Subtraction (
sub
, no overflow check) - ❌ Division
- ❌ Multiplication
- ✔️ Addition (
- ⭕ Branching
- ✔️ Branch if true (
brtrue
,brtrue.s
) - ❌ Branch if false
- ✔️ Unconditional branch (
br
,br.s
)
- ✔️ Branch if true (
- ⭕ Comparison
- ❌ Equal
- ✔️ Greater than (
cgt.un
) - ❌ Less than
- ⭕ Load
- ✔️ Argument (
ldarg
,ldarg.s
,ldarg.0
,ldarg.1
,ldarg.2
,ldarg.3
) - ✔️ Constant (
ldc.i4
,ldc.i4.s
,ldc.i4.0
,ldc.i4.1
,ldc.i4.2
,ldc.i4.3
,ldc.i4.4
,ldc.i4.5
,ldc.i4.6
,ldc.i4.7
,ldc.i4.8
) - ❌ Element
- ✔️ Field (static) (
ldsfld
)- ✔️ Address (
ldsflda
)
- ✔️ Address (
- ✔️ Field (instance) (
ldfld
)- ✔️ Address (
ldflda
)
- ✔️ Address (
- ❌ Indirect (
ldind.i
,ldind.i1
) - ✔️ Local (
ldloc
,ldloc.s
,ldloc.0
,ldloc.1
,ldloc.2
,ldloc.3
)
- ✔️ Argument (
- ⭕ Store
- ✔️ Argument (
starg
,starg.s
) - ❌ Element
- ✔️ Field (static) (
stsfld
) - ✔️ Field (instance) (
stfld
) - ❌ Indirect (
stind.i
,stind.i1
) - ✔️ Local (
stloc
,stloc.s
,stloc.0
,stloc.1
,stloc.2
,stloc.3
)
- ✔️ Argument (
- ⭕ Miscellaneous Object Model
- ✔️ Initialize value type (
initobj
)
- ✔️ Initialize value type (
- ⭕ Arithmetic
Load the solution into Visual Studio Community 2017 and it should build and run fine.
The public interface is very rudimentary. You can either invoke it programmatically through VCSCompiler.Compiler.CompileFromFiles()
, or through the command line program like so:
dotnet VCSCompilerCLI.dll path_to_source_file path_to_vcsframework_dll path_to_dasm_executable
This project is licensed under the MIT License.