├── docs ├── CNAME ├── favicon.ico ├── images │ ├── badges.png │ ├── flash.png │ ├── winzip.png │ ├── acrobat.png │ ├── noframes.png │ ├── notepad.png │ ├── quicktime.png │ ├── realplayer.png │ ├── screenshot.png │ ├── shockwave.png │ └── Man-scratching-head.gif └── MTMC_FAQ.md ├── disk ├── img │ ├── msu.jpg │ ├── msu2.png │ ├── msu3.png │ └── mtmc-splash.png ├── src │ ├── games │ │ ├── dino │ │ │ ├── rex.png │ │ │ └── dino.asm │ │ ├── wumpus.png │ │ ├── fishdish │ │ │ ├── fish.png │ │ │ ├── fish-left.png │ │ │ ├── background.png │ │ │ ├── fish-right.png │ │ │ └── shark-left.png │ │ └── ant.asm │ ├── utils │ │ ├── pwd.asm │ │ ├── rm.asm │ │ ├── cd.asm │ │ ├── echo.asm │ │ └── ls.asm │ ├── hello_world.asm │ ├── sort │ ├── square.sea │ ├── sea │ │ └── embedded_structs.sea │ ├── towerdef.sea │ └── sort.asm ├── bin │ ├── pwd │ ├── hello_world │ ├── echo │ ├── rm │ ├── cd │ ├── ant │ ├── square │ └── ls └── data │ ├── gun.cells │ ├── cloverleaf.cells │ ├── r2d2.cells │ ├── boss.cells │ ├── airforce.cells │ ├── clock.cells │ ├── gourmet.cells │ ├── sparky.cells │ ├── brain.cells │ ├── galaxy.cells │ ├── queenbee.cells │ ├── quad.cells │ ├── blinkership.cells │ ├── hertz.cells │ ├── 101.cells │ ├── twogun.cells │ ├── barberpole.cells │ └── beacons.cells ├── src ├── main │ ├── resources │ │ ├── disk.zip │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── img │ │ │ │ ├── disk-icon.ico │ │ │ │ ├── blank-sheet.ico │ │ │ │ ├── search-folder-icon.ico │ │ │ │ ├── single-folder-icon.ico │ │ │ │ ├── new-file.svg │ │ │ │ ├── xmark.svg │ │ │ │ ├── floppy-disk-solid.svg │ │ │ │ ├── new-directory.svg │ │ │ │ ├── expand.svg │ │ │ │ └── contract.svg │ │ │ ├── apple-touch-icon.png │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── site.webmanifest │ │ │ └── js │ │ │ │ └── fixi-0.9.0.js │ │ ├── META-INF │ │ │ └── services │ │ │ │ └── java.nio.file.spi.FileTypeDetector │ │ └── templates │ │ │ ├── memory.html │ │ │ ├── editors │ │ │ ├── noeditor.html │ │ │ ├── image.html │ │ │ └── monaco.html │ │ │ ├── console.html │ │ │ ├── display.html │ │ │ ├── newdir.html │ │ │ ├── index.html │ │ │ ├── newfile.html │ │ │ ├── control.html │ │ │ └── filetree.html │ └── java │ │ └── mtmc │ │ ├── asm │ │ ├── HasLocation.java │ │ ├── ASMError.java │ │ ├── AssemblyResult.java │ │ ├── instructions │ │ │ ├── ErrorInstruction.java │ │ │ ├── JumpRegisterInstruction.java │ │ │ ├── ALUOp.java │ │ │ ├── JumpInstruction.java │ │ │ ├── InstructionType.java │ │ │ ├── Instruction.java │ │ │ ├── LoadStoreRegisterInstruction.java │ │ │ ├── MetaInstruction.java │ │ │ ├── ALUInstruction.java │ │ │ └── LoadStoreInstruction.java │ │ ├── ASMElement.java │ │ ├── data │ │ │ └── Data.java │ │ └── graphics │ │ │ └── Graphic.java │ │ ├── lang │ │ ├── sea │ │ │ ├── ast │ │ │ │ ├── Error.java │ │ │ │ ├── SyntaxError.java │ │ │ │ ├── TypeDeclaration.java │ │ │ │ ├── StatementBreak.java │ │ │ │ ├── StatementContinue.java │ │ │ │ ├── TypeExprInt.java │ │ │ │ ├── TypeExprVoid.java │ │ │ │ ├── StatementGoto.java │ │ │ │ ├── StatementExpression.java │ │ │ │ ├── Declaration.java │ │ │ │ ├── TypeExprRef.java │ │ │ │ ├── TypeExprChar.java │ │ │ │ ├── ExpressionParens.java │ │ │ │ ├── StatementReturn.java │ │ │ │ ├── ExpressionChar.java │ │ │ │ ├── ExpressionInteger.java │ │ │ │ ├── TypeExprArray.java │ │ │ │ ├── StatementBlock.java │ │ │ │ ├── TypePointer.java │ │ │ │ ├── ExpressionCast.java │ │ │ │ ├── StatementWhile.java │ │ │ │ ├── ExpressionIndex.java │ │ │ │ ├── StatementDoWhile.java │ │ │ │ ├── ExpressionPostfix.java │ │ │ │ ├── ExpressionPrefix.java │ │ │ │ ├── ExpressionString.java │ │ │ │ ├── ExpressionIdent.java │ │ │ │ ├── ExpressionCall.java │ │ │ │ ├── StatementSyntaxError.java │ │ │ │ ├── ExpressionAccess.java │ │ │ │ ├── ExpressionTernary.java │ │ │ │ ├── StatementIf.java │ │ │ │ ├── DeclarationSyntaxError.java │ │ │ │ ├── TypeExpr.java │ │ │ │ ├── ExpressionBin.java │ │ │ │ ├── StatementVar.java │ │ │ │ ├── DeclarationVar.java │ │ │ │ ├── ExpressionTypeError.java │ │ │ │ ├── DeclarationTypedef.java │ │ │ │ ├── Unit.java │ │ │ │ ├── StatementFor.java │ │ │ │ ├── ExpressionInitializer.java │ │ │ │ ├── Expression.java │ │ │ │ ├── ExpressionSyntaxError.java │ │ │ │ ├── Statement.java │ │ │ │ ├── DeclarationStruct.java │ │ │ │ └── DeclarationFunc.java │ │ │ ├── Symbol.java │ │ │ └── SeaLanguage.java │ │ ├── Language.java │ │ ├── Token.java │ │ ├── Span.java │ │ ├── CompilationException.java │ │ ├── ParseException.java │ │ └── Location.java │ │ ├── util │ │ ├── SafeClosable.java │ │ ├── StringEscapeUtils.java │ │ └── BinaryUtils.java │ │ ├── os │ │ ├── shell │ │ │ ├── UsageException.java │ │ │ ├── ShellCommand.java │ │ │ └── builtins │ │ │ │ ├── PauseCommand.java │ │ │ │ ├── ExitCommand.java │ │ │ │ ├── HelpCommand.java │ │ │ │ ├── RunCommand.java │ │ │ │ ├── WebCommand.java │ │ │ │ ├── StepCommand.java │ │ │ │ ├── SpeedCommand.java │ │ │ │ ├── GetCommand.java │ │ │ │ ├── SeacCommand.java │ │ │ │ ├── LoadCommand.java │ │ │ │ ├── AssembleCommand.java │ │ │ │ ├── SetCommand.java │ │ │ │ └── DisplayCommand.java │ │ ├── fs │ │ │ ├── ExecutableFileTypeDetector.java │ │ │ ├── PlainTextFileTypeDetector.java │ │ │ └── Listing.java │ │ ├── exec │ │ │ └── Executable.java │ │ └── SysCall.java │ │ ├── emulator │ │ ├── RewindStep.java │ │ ├── MTMCObserver.java │ │ ├── MTMCIO.java │ │ ├── DebugInfo.java │ │ ├── Register.java │ │ ├── MTMCClock.java │ │ └── MTMCConsole.java │ │ └── tokenizer │ │ ├── MTMCToken.java │ │ └── MTMCTokenizer.java └── test │ └── java │ └── mtmc │ ├── lang │ └── sea │ │ ├── ValidationException.java │ │ └── Util.java │ ├── os │ └── fs │ │ ├── ListingTest.java │ │ └── FileSystemTest.java │ └── emulator │ └── EndToEndTests.java ├── LICENSE.txt ├── .gitignore ├── README.md └── pom.xml /docs/CNAME: -------------------------------------------------------------------------------- 1 | mtmc.cs.montana.edu -------------------------------------------------------------------------------- /disk/img/msu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/disk/img/msu.jpg -------------------------------------------------------------------------------- /disk/img/msu2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/disk/img/msu2.png -------------------------------------------------------------------------------- /disk/img/msu3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/disk/img/msu3.png -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /docs/images/badges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/images/badges.png -------------------------------------------------------------------------------- /docs/images/flash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/images/flash.png -------------------------------------------------------------------------------- /docs/images/winzip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/images/winzip.png -------------------------------------------------------------------------------- /disk/img/mtmc-splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/disk/img/mtmc-splash.png -------------------------------------------------------------------------------- /docs/images/acrobat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/images/acrobat.png -------------------------------------------------------------------------------- /docs/images/noframes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/images/noframes.png -------------------------------------------------------------------------------- /docs/images/notepad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/images/notepad.png -------------------------------------------------------------------------------- /disk/src/games/dino/rex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/disk/src/games/dino/rex.png -------------------------------------------------------------------------------- /disk/src/games/wumpus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/disk/src/games/wumpus.png -------------------------------------------------------------------------------- /docs/images/quicktime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/images/quicktime.png -------------------------------------------------------------------------------- /docs/images/realplayer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/images/realplayer.png -------------------------------------------------------------------------------- /docs/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/images/screenshot.png -------------------------------------------------------------------------------- /docs/images/shockwave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/images/shockwave.png -------------------------------------------------------------------------------- /src/main/resources/disk.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/src/main/resources/disk.zip -------------------------------------------------------------------------------- /disk/src/games/fishdish/fish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/disk/src/games/fishdish/fish.png -------------------------------------------------------------------------------- /disk/src/games/fishdish/fish-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/disk/src/games/fishdish/fish-left.png -------------------------------------------------------------------------------- /docs/images/Man-scratching-head.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/docs/images/Man-scratching-head.gif -------------------------------------------------------------------------------- /src/main/resources/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/src/main/resources/public/favicon.ico -------------------------------------------------------------------------------- /disk/src/games/fishdish/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/disk/src/games/fishdish/background.png -------------------------------------------------------------------------------- /disk/src/games/fishdish/fish-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/disk/src/games/fishdish/fish-right.png -------------------------------------------------------------------------------- /disk/src/games/fishdish/shark-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/disk/src/games/fishdish/shark-left.png -------------------------------------------------------------------------------- /src/main/resources/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/src/main/resources/public/favicon-16x16.png -------------------------------------------------------------------------------- /src/main/resources/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/src/main/resources/public/favicon-32x32.png -------------------------------------------------------------------------------- /src/main/resources/public/img/disk-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/src/main/resources/public/img/disk-icon.ico -------------------------------------------------------------------------------- /disk/src/utils/pwd.asm: -------------------------------------------------------------------------------- 1 | .text 2 | main: 3 | li a0 256 4 | li a1 256 5 | 6 | sys cwd 7 | sys wstr 8 | 9 | sys exit 10 | -------------------------------------------------------------------------------- /src/main/resources/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/src/main/resources/public/apple-touch-icon.png -------------------------------------------------------------------------------- /src/main/resources/public/img/blank-sheet.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/src/main/resources/public/img/blank-sheet.ico -------------------------------------------------------------------------------- /disk/src/hello_world.asm: -------------------------------------------------------------------------------- 1 | .data 2 | hello_world: "Hello World!" 3 | 4 | .text 5 | main: 6 | li a0 hello_world 7 | sys wstr 8 | sys exit 9 | -------------------------------------------------------------------------------- /src/main/resources/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/src/main/resources/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /src/main/resources/public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/src/main/resources/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/main/resources/public/img/search-folder-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/src/main/resources/public/img/search-folder-icon.ico -------------------------------------------------------------------------------- /src/main/resources/public/img/single-folder-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msu/mtmc/HEAD/src/main/resources/public/img/single-folder-icon.ico -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/java.nio.file.spi.FileTypeDetector: -------------------------------------------------------------------------------- 1 | mtmc.os.fs.ExecutableFileTypeDetector 2 | mtmc.os.fs.PlainTextFileTypeDetector -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/HasLocation.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm; 2 | 3 | public interface HasLocation { 4 | int getLocation(); 5 | int getSizeInBytes(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/Error.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.ParseException; 4 | 5 | public interface Error { 6 | ParseException exception(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/mtmc/util/SafeClosable.java: -------------------------------------------------------------------------------- 1 | package mtmc.util; 2 | 3 | import java.io.Closeable; 4 | 5 | public interface SafeClosable extends Closeable { 6 | @Override 7 | void close(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/SyntaxError.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.ParseException; 4 | 5 | public interface SyntaxError extends Error { 6 | ParseException exception(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/TypeDeclaration.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | 5 | public interface TypeDeclaration { 6 | String name(); 7 | SeaType type(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/Language.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang; 2 | 3 | import mtmc.os.exec.Executable; 4 | 5 | public interface Language { 6 | Executable compileExecutable(String filename, String source) throws ParseException, CompilationException; 7 | } 8 | -------------------------------------------------------------------------------- /disk/src/utils/rm.asm: -------------------------------------------------------------------------------- 1 | .data 2 | error: "UNABLE TO DELETE\n" 3 | 4 | .text 5 | main: 6 | sys dfile 7 | eqi rv 0 8 | jnz done 9 | 10 | li a0 error 11 | li a1 256 12 | sys wstr 13 | 14 | done: 15 | sys exit 16 | 17 | -------------------------------------------------------------------------------- /disk/src/sort: -------------------------------------------------------------------------------- 1 | {"format":"Orc1","code":[],"data":[],"graphics":[],"sourceName":"\\src\\sort.asm","debugInfo":{"debugStrings":[],"assemblyFile":"\\src\\sort.asm","assemblySource":"","assemblyLineNumbers":[],"originalFile":"","originalLineNumbers":[],"globals":[],"locals":[]}} -------------------------------------------------------------------------------- /disk/src/utils/cd.asm: -------------------------------------------------------------------------------- 1 | .data 2 | invalid_dir: "INVALID DIRECTORY" 3 | 4 | .text 5 | main: 6 | sys chdir 7 | eqi rv 0 8 | jnz done 9 | 10 | li a0 invalid_dir 11 | li a1 256 12 | sys wstr 13 | 14 | done: 15 | sys exit 16 | 17 | -------------------------------------------------------------------------------- /disk/src/utils/echo.asm: -------------------------------------------------------------------------------- 1 | .data 2 | NEW_LINE: "\n" 3 | 4 | .text 5 | 6 | neqi a0 0 7 | jz no_string 8 | # echo the string passed in as an arg in a0 9 | sys wstr 10 | 11 | no_string: 12 | # write a newline 13 | la a0 NEW_LINE 14 | sys wstr 15 | 16 | # exit 17 | sys exit 18 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/ASMError.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm; 2 | 3 | import mtmc.tokenizer.MTMCToken; 4 | 5 | public record ASMError(MTMCToken token, String error) { 6 | public String formattedErrorMessage() { 7 | return "Line " + token.line() + ": " + error; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementBreak.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class StatementBreak extends Statement { 6 | public StatementBreak(Token breakToken) { 7 | super(breakToken, breakToken); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementContinue.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class StatementContinue extends Statement { 6 | public StatementContinue(Token continueToken) { 7 | super(continueToken, continueToken); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/TypeExprInt.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class TypeExprInt extends TypeExpr { 7 | public TypeExprInt(Token token) { 8 | super(token, token, SeaType.INT); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/TypeExprVoid.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class TypeExprVoid extends TypeExpr { 7 | public TypeExprVoid(Token token) { 8 | super(token, token, SeaType.VOID); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/Token.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang; 2 | 3 | public interface Token { 4 | Location getStart(); 5 | Location getEnd(); 6 | String getContent(); 7 | 8 | default int start() { 9 | return getStart().index(); 10 | } 11 | 12 | default int end() { 13 | return getEnd().index(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementGoto.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class StatementGoto extends Statement { 6 | public final Token label; 7 | 8 | public StatementGoto(Token start, Token label) { 9 | super(start, label); 10 | this.label = label; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementExpression.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | public final class StatementExpression extends Statement { 4 | public final Expression expression; 5 | 6 | public StatementExpression(Expression expression) { 7 | super(expression.start, expression.end); 8 | this.expression = expression; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/Declaration.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public abstract sealed class Declaration extends Ast permits DeclarationFunc, DeclarationStruct, DeclarationSyntaxError, DeclarationTypedef, DeclarationVar { 6 | public Declaration(Token start, Token end) { 7 | super(start, end); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/TypeExprRef.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class TypeExprRef extends TypeExpr { 6 | public final TypeDeclaration decl; 7 | 8 | public TypeExprRef(Token name, TypeDeclaration decl) { 9 | super(name, name, decl.type()); 10 | this.decl = decl; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/TypeExprChar.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class TypeExprChar extends TypeExpr { 7 | public final Token token; 8 | 9 | public TypeExprChar(Token token) { 10 | super(token, token, SeaType.CHAR); 11 | this.token = token; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionParens.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class ExpressionParens extends Expression { 6 | public final Expression inner; 7 | 8 | public ExpressionParens(Token start, Expression inner, Token end) { 9 | super(start, end, inner.type()); 10 | this.inner = inner; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementReturn.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class StatementReturn extends Statement { 6 | public final Expression value; 7 | 8 | public StatementReturn(Token start, Expression value) { 9 | super(start, value == null ? start : value.end); 10 | this.value = value; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/mtmc/lang/sea/ValidationException.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea; 2 | 3 | import mtmc.lang.sea.ast.Error; 4 | 5 | import java.util.List; 6 | 7 | public class ValidationException extends RuntimeException { 8 | public final List errors; 9 | 10 | public ValidationException(List errors, String message) { 11 | super(message); 12 | this.errors = errors; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionChar.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class ExpressionChar extends Expression { 7 | public ExpressionChar(Token token) { 8 | super(token, token, SeaType.CHAR); 9 | } 10 | 11 | public Character content() { 12 | return start.content().charAt(0); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/UsageException.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell; 2 | 3 | public class UsageException extends RuntimeException { 4 | private final ShellCommand cmd; 5 | public UsageException(ShellCommand shellCommand) { 6 | super("Usage:\n\n" + shellCommand.getHelp()); 7 | this.cmd = shellCommand; 8 | } 9 | public ShellCommand getCmd() { 10 | return cmd; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionInteger.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class ExpressionInteger extends Expression { 7 | public final int value; 8 | 9 | public ExpressionInteger(Token start) { 10 | super(start, start, SeaType.INT); 11 | this.value = Integer.parseInt(start.content()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/TypeExprArray.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class TypeExprArray extends TypeExpr { 7 | public final TypeExpr inner; 8 | 9 | public TypeExprArray(TypeExpr inner, Token end) { 10 | super(inner.start, end, new SeaType.Pointer(inner.type())); 11 | this.inner = inner; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mtmc/emulator/RewindStep.java: -------------------------------------------------------------------------------- 1 | package mtmc.emulator; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class RewindStep { 7 | 8 | List subSteps = new ArrayList<>(); 9 | 10 | public void rewind() { 11 | subSteps.reversed().forEach(Runnable::run); 12 | } 13 | 14 | public void addSubStep(Runnable subStep) { 15 | subSteps.add(subStep); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/templates/memory.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Memory 4 | 9 |
10 |
11 | {{computer.getMemoryTable() | raw}} 12 |
13 |
-------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementBlock.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | import java.util.List; 6 | 7 | public final class StatementBlock extends Statement { 8 | public final List statements; 9 | 10 | public StatementBlock(Token start, List children, Token end) { 11 | super(start, end); 12 | this.statements = List.copyOf(children); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/TypePointer.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class TypePointer extends TypeExpr { 7 | public final TypeExpr component; 8 | 9 | public TypePointer(TypeExpr component, Token star) { 10 | super(component.start, star, new SeaType.Pointer(component.type())); 11 | this.component = component; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/ShellCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell; 2 | 3 | import mtmc.emulator.MonTanaMiniComputer; 4 | import mtmc.tokenizer.MTMCTokenizer; 5 | 6 | public abstract class ShellCommand { 7 | public abstract void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) throws Exception; 8 | public abstract String getHelp(); 9 | public void usageException() { 10 | throw new UsageException(this); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"msu MTMC-16 Mini Computer","short_name":"MTMC-16","description":"The MonTana state Mini Computer is a virtual computer intended to show how digital computation works in a fun and visual way.","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#c3c3c3","background_color":"#c3c3c3","display":"fullscreen"} -------------------------------------------------------------------------------- /disk/src/square.sea: -------------------------------------------------------------------------------- 1 | // stdlib functions 2 | int printf(char* str, ...); 3 | void putn(int i); 4 | int atoi(char* str); 5 | 6 | // square an integer 7 | int square(int x) { 8 | return x * x; 9 | } 10 | 11 | // main program 12 | int main(char* arg) { 13 | if(arg == 0) { 14 | printf("usage: square \n"); 15 | return -1; 16 | } 17 | int val = atoi(arg); 18 | int result = square(val); 19 | putn(result); 20 | return 0; 21 | } -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionCast.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class ExpressionCast extends Expression { 6 | public final TypeExpr type; 7 | public final Expression value; 8 | 9 | public ExpressionCast(Token start, TypeExpr type, Expression value) { 10 | super(start, value.end, type.type()); 11 | this.type = type; 12 | this.value = value; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementWhile.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class StatementWhile extends Statement { 6 | public final Expression condition; 7 | public final Statement body; 8 | 9 | public StatementWhile(Token start, Expression condition, Statement body) { 10 | super(start, body.end); 11 | this.condition = condition; 12 | this.body = body; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/public/img/new-file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /disk/bin/pwd: -------------------------------------------------------------------------------- 1 | {"format":"Orc1","code":[-113,96,1,0,-113,112,1,0,0,18,0,6,0,0],"data":[],"graphics":[],"sourceName":"/src/utils/pwd.asm","debugInfo":{"debugStrings":[],"assemblyFile":"/src/utils/pwd.asm","assemblySource":".text\nmain:\n li a0 256\n li a1 256\n\n sys cwd\n sys wstr\n\n sys exit\n","assemblyLineNumbers":[3,3,3,3,4,4,4,4,6,6,7,7,9,9],"originalFile":"","originalLineNumbers":[0,0,0,0,0,0,0,0,0,0,0,0,0,0],"globals":[],"locals":[[],[],[],[],[],[],[],[],[],[],[],[],[],[]]}} -------------------------------------------------------------------------------- /disk/data/gun.cells: -------------------------------------------------------------------------------- 1 | !Name: Gosper glider gun 2 | !Author: Bill Gosper 3 | !The first known gun and the first known finite pattern with unbounded growth. 4 | !www.conwaylife.com/wiki/index.php?title=Gosper_glider_gun 5 | ........................O 6 | ......................O.O 7 | ............OO......OO............OO 8 | ...........O...O....OO............OO 9 | OO........O.....O...OO 10 | OO........O...O.OO....O.O 11 | ..........O.....O.......O 12 | ...........O...O 13 | ............OO -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionIndex.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class ExpressionIndex extends Expression { 7 | public final Expression array, index; 8 | 9 | public ExpressionIndex(Expression array, Expression index, Token end, SeaType type) { 10 | super(array.start, end, type); 11 | this.array = array; 12 | this.index = index; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementDoWhile.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class StatementDoWhile extends Statement { 6 | public final Statement body; 7 | public final Expression condition; 8 | 9 | public StatementDoWhile(Token start, Statement body, Expression condition, Token end) { 10 | super(start, end); 11 | this.body = body; 12 | this.condition = condition; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /disk/bin/hello_world: -------------------------------------------------------------------------------- 1 | {"format":"Orc1","code":[-113,96,0,8,0,6,0,0],"data":[72,101,108,108,111,32,87,111,114,108,100,33,0],"graphics":[],"sourceName":"/src/hello_world.asm","debugInfo":{"debugStrings":[],"assemblyFile":"/src/hello_world.asm","assemblySource":".data\n hello_world: \"Hello World!\"\n\n.text\nmain:\n li a0 hello_world\n sys wstr\n sys exit\n","assemblyLineNumbers":[6,6,6,6,7,7,8,8],"originalFile":"","originalLineNumbers":[0,0,0,0,0,0,0,0],"globals":[],"locals":[[],[],[],[],[],[],[],[]]}} -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionPostfix.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class ExpressionPostfix extends Expression { 7 | public Expression inner; 8 | 9 | public ExpressionPostfix(Expression lhs, Token op, SeaType type) { 10 | super(lhs.start, op, type); 11 | this.inner = lhs; 12 | } 13 | 14 | public String op() { 15 | return end.content(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionPrefix.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class ExpressionPrefix extends Expression { 7 | public final Expression inner; 8 | 9 | public ExpressionPrefix(Token operator, Expression rhs, SeaType type) { 10 | super(operator, rhs.end, type); 11 | this.inner = rhs; 12 | } 13 | 14 | public String op() { 15 | return start.content(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/Span.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang; 2 | 3 | public record Span(Token start, Token end) { 4 | 5 | public static Span of(Token token) { 6 | return new Span(token, token); 7 | } 8 | 9 | public static Span of(Token start, Token end) { 10 | return new Span(start, end); 11 | } 12 | 13 | public boolean isOnSingleLine(String source) { 14 | int[] lines = Location.getLineNos(source, start.start(), end.end()); 15 | return lines[0] == lines[1]; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionString.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class ExpressionString extends Expression { 7 | public ExpressionString(Token token) { 8 | super(token, token, new SeaType.Pointer(SeaType.CHAR)); 9 | } 10 | 11 | public byte[] getBytes() { 12 | return start.content().getBytes(); 13 | } 14 | 15 | public String content() { 16 | return start.content(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionIdent.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class ExpressionIdent extends Expression { 7 | public final boolean isAddressable; 8 | 9 | public ExpressionIdent(Token token, SeaType type, boolean isAddressable) { 10 | super(token, token, type); 11 | this.isAddressable = isAddressable; 12 | } 13 | 14 | public String name() { 15 | return start.content(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /disk/src/sea/embedded_structs.sea: -------------------------------------------------------------------------------- 1 | int printf(char *s, ...); 2 | 3 | struct Address { 4 | char *city; 5 | char *state; 6 | }; 7 | 8 | struct Person { 9 | char *name; 10 | int age; 11 | Address addr; 12 | }; 13 | 14 | Person gc1 = {"ssorg nosrac", -1, {"Bozeman", "MT"}}; 15 | 16 | int main() { 17 | gc1.name = "carson gross"; 18 | gc1.age = 46; 19 | gc1.addr = {"San Francisco", "CA"}; 20 | printf("%s %d from %s, %s\n", gc1.name, gc1.age, 21 | gc1.addr.city, gc1.addr.state); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/PauseCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.emulator.MonTanaMiniComputer; 4 | import mtmc.os.shell.ShellCommand; 5 | import mtmc.tokenizer.MTMCTokenizer; 6 | 7 | public class PauseCommand extends ShellCommand { 8 | @Override 9 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) { 10 | computer.pause(); 11 | } 12 | 13 | @Override 14 | public String getHelp() { 15 | return "pause - pauses the computer"; 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/main/resources/public/img/xmark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/CompilationException.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang; 2 | 3 | public class CompilationException extends Exception { 4 | protected Span span; 5 | 6 | public CompilationException(String message, Span span) { 7 | super(message); 8 | this.span = span; 9 | } 10 | 11 | public CompilationException(CompilationException parent, String message) { 12 | super(message, parent); 13 | this.span = parent.span; 14 | } 15 | 16 | public Span getSpan() { 17 | return span; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionCall.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | import java.util.List; 7 | 8 | public final class ExpressionCall extends Expression { 9 | public final Expression functor; 10 | public final List args; 11 | 12 | public ExpressionCall(Expression functor, List args, Token end, SeaType type) { 13 | super(functor.start, end, type); 14 | this.functor = functor; 15 | this.args = args; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementSyntaxError.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.ParseException; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class StatementSyntaxError extends Statement implements SyntaxError { 7 | public final ParseException exception; 8 | 9 | public StatementSyntaxError(Token token, ParseException exception) { 10 | super(token, token); 11 | this.exception = exception; 12 | } 13 | 14 | @Override 15 | public ParseException exception() { 16 | return exception; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/AssemblyResult.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm; 2 | 3 | import mtmc.emulator.DebugInfo; 4 | 5 | import java.util.List; 6 | 7 | public record AssemblyResult(byte[] code, byte[] data, byte[][] graphics, DebugInfo debugInfo, List errors) { 8 | public String printErrors() { 9 | StringBuilder builder = new StringBuilder("Errors:\n"); 10 | for (ASMError error : errors) { 11 | builder.append(" Line " + error.token().line() + ": " + error.error()).append('\n'); 12 | } 13 | return builder.toString(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionAccess.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class ExpressionAccess extends Expression { 7 | public final Expression value; 8 | public final Token access; 9 | public final Token prop; 10 | 11 | public ExpressionAccess(Expression value, Token access, Token prop, SeaType type) { 12 | super(value.start, prop, type); 13 | this.value = value; 14 | this.access = access; 15 | this.prop = prop; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionTernary.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | 5 | public final class ExpressionTernary extends Expression { 6 | public final Expression cond; 7 | public final Expression then; 8 | public final Expression otherwise; 9 | 10 | public ExpressionTernary(Expression cond, Expression then, Expression otherwise, SeaType type) { 11 | super(cond.start, otherwise.end, type); 12 | this.cond = cond; 13 | this.then = then; 14 | this.otherwise = otherwise; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/ExitCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.emulator.MonTanaMiniComputer; 4 | import mtmc.os.shell.ShellCommand; 5 | import mtmc.tokenizer.MTMCTokenizer; 6 | 7 | public class ExitCommand extends ShellCommand { 8 | @Override 9 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) { 10 | computer.getConsole().println("Goodbye!"); 11 | System.exit(1); 12 | } 13 | 14 | @Override 15 | public String getHelp() { 16 | return "exit - exits the system"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/instructions/ErrorInstruction.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.instructions; 2 | 3 | import mtmc.asm.Assembler; 4 | import mtmc.tokenizer.MTMCToken; 5 | 6 | import java.util.List; 7 | 8 | public class ErrorInstruction extends Instruction { 9 | public ErrorInstruction(List labels, MTMCToken instruction, String error) { 10 | super(null, labels, instruction); 11 | addError(instruction, error); 12 | } 13 | 14 | @Override 15 | public void genCode(byte[] output, Assembler assembler) { 16 | // do nothing 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementIf.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class StatementIf extends Statement { 6 | public final Expression condition; 7 | public final Statement body; 8 | public final Statement elseBody; 9 | 10 | public StatementIf(Token start, Expression condition, Statement body, Statement elseBody) { 11 | super(start, elseBody == null ? body.end : elseBody.end); 12 | this.condition = condition; 13 | this.body = body; 14 | this.elseBody = elseBody; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/public/img/floppy-disk-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/DeclarationSyntaxError.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.ParseException; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class DeclarationSyntaxError extends Declaration implements SyntaxError { 7 | public final ParseException exception; 8 | 9 | public DeclarationSyntaxError(Token token, ParseException parseException) { 10 | super(token, token); 11 | this.exception = parseException; 12 | } 13 | 14 | @Override 15 | public ParseException exception() { 16 | return exception; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/HelpCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.emulator.MonTanaMiniComputer; 4 | import mtmc.os.shell.Shell; 5 | import mtmc.os.shell.ShellCommand; 6 | import mtmc.tokenizer.MTMCTokenizer; 7 | 8 | public class HelpCommand extends ShellCommand { 9 | @Override 10 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) throws Exception { 11 | Shell.printShellHelp(computer); 12 | } 13 | 14 | @Override 15 | public String getHelp() { 16 | return "? or help - print help"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /disk/bin/echo: -------------------------------------------------------------------------------- 1 | {"format":"Orc1","code":[57,96,-48,6,0,6,-113,96,0,14,0,6,0,0],"data":[10,0],"graphics":[],"sourceName":"/src/utils/echo.asm","debugInfo":{"debugStrings":[],"assemblyFile":"/src/utils/echo.asm","assemblySource":".data\nNEW_LINE: \"\\n\"\n\n.text\n\nneqi a0 0\njz no_string\n# echo the string passed in as an arg in a0\nsys wstr\n\nno_string:\n# write a newline\nla a0 NEW_LINE\nsys wstr\n\n# exit\nsys exit\n","assemblyLineNumbers":[6,6,7,7,9,9,13,13,13,13,14,14,17,17],"originalFile":"","originalLineNumbers":[0,0,0,0,0,0,0,0,0,0,0,0,0,0],"globals":[],"locals":[[],[],[],[],[],[],[],[],[],[],[],[],[],[]]}} -------------------------------------------------------------------------------- /disk/src/towerdef.sea: -------------------------------------------------------------------------------- 1 | 2 | 3 | struct Enemy { 4 | int x; 5 | int y; 6 | int health; 7 | }; 8 | 9 | struct Tower { 10 | int x; 11 | int y; 12 | int power; 13 | }; 14 | 15 | int main() { 16 | int towern = 1; 17 | Tower towers[20] = { { 10, 10, 1 } }; 18 | 19 | int enemyn = 0; 20 | Enemy enemies[100] = {}; 21 | 22 | int stage = 0; 23 | 24 | while (1) { 25 | if (stage == 0) { 26 | // TODO: input 27 | } else if (stage == 1) { 28 | // TODO: waves 29 | } else { 30 | } 31 | } 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/TypeExpr.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | import java.util.Objects; 7 | 8 | public abstract sealed class TypeExpr extends Ast permits TypeExprArray, TypeExprChar, TypeExprInt, TypeExprRef, TypeExprVoid, TypePointer { 9 | private final SeaType type; 10 | 11 | public TypeExpr(Token start, Token end, SeaType type) { 12 | super(start, end); 13 | this.type = Objects.requireNonNull(type); 14 | } 15 | 16 | public SeaType type() { 17 | return type; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/templates/editors/noeditor.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
{{computer.currentFilename}}
5 |
6 |
7 | 8 |
9 |
10 |
No editor available for this file
11 |
-------------------------------------------------------------------------------- /src/main/resources/public/img/new-directory.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionBin.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class ExpressionBin extends Expression { 7 | public final Expression lhs; 8 | public final Token op; 9 | public final Expression rhs; 10 | 11 | public ExpressionBin(Expression lhs, Token op, Expression rhs, SeaType type) { 12 | super(lhs.start, rhs.end, type); 13 | this.lhs = lhs; 14 | this.op = op; 15 | this.rhs = rhs; 16 | } 17 | 18 | public String op() { 19 | return op.content(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementVar.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class StatementVar extends Statement { 6 | public final TypeExpr type; 7 | public final Token name; 8 | public final Expression initValue; 9 | 10 | public StatementVar(TypeExpr type, Token name, Expression initValue) { 11 | super(type.start, initValue == null ? name : initValue.end); 12 | this.type = type; 13 | this.name = name; 14 | this.initValue = initValue; 15 | } 16 | 17 | public String name() { 18 | return name.content(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/DeclarationVar.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class DeclarationVar extends Declaration { 6 | public final TypeExpr type; 7 | public final Token name; 8 | public final Expression initializer; 9 | 10 | public DeclarationVar(TypeExpr type, Token name, Expression initializer) { 11 | super(type.start, initializer == null ? name : initializer.end); 12 | this.type = type; 13 | this.name = name; 14 | this.initializer = initializer; 15 | } 16 | 17 | public String name() { 18 | return this.name.content(); 19 | } 20 | } -------------------------------------------------------------------------------- /disk/bin/rm: -------------------------------------------------------------------------------- 1 | {"format":"Orc1","code":[0,21,56,-96,-32,16,-113,96,0,18,-113,112,1,0,0,6,0,0],"data":[85,78,65,66,76,69,32,84,79,32,68,69,76,69,84,69,10,0],"graphics":[],"sourceName":"/src/utils/rm.asm","debugInfo":{"debugStrings":[],"assemblyFile":"/src/utils/rm.asm","assemblySource":".data\n error: \"UNABLE TO DELETE\\n\"\n\n.text\nmain:\n sys dfile\n eqi rv 0\n jnz done\n \n li a0 error\n li a1 256\n sys wstr\n \n done:\n sys exit\n\n","assemblyLineNumbers":[6,6,7,7,8,8,10,10,10,10,11,11,11,11,12,12,15,15],"originalFile":"","originalLineNumbers":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"globals":[],"locals":[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]}} -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/RunCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.emulator.MonTanaMiniComputer; 4 | import mtmc.os.shell.ShellCommand; 5 | import mtmc.tokenizer.MTMCTokenizer; 6 | 7 | public class RunCommand extends ShellCommand { 8 | @Override 9 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) { 10 | switch (computer.getStatus()) { 11 | case READY, EXECUTING -> computer.run(); 12 | default -> { 13 | } 14 | } 15 | } 16 | 17 | @Override 18 | public String getHelp() { 19 | return "run - runs the program until it halts"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/public/img/expand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Zero-Clause BSD 2 | ============= 3 | 4 | Permission to use, copy, modify, and/or distribute this software for 5 | any purpose with or without fee is hereby granted. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL 8 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 9 | OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE 10 | FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY 11 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 12 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 | OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /disk/bin/cd: -------------------------------------------------------------------------------- 1 | {"format":"Orc1","code":[0,19,56,-96,-32,16,-113,96,0,18,-113,112,1,0,0,6,0,0],"data":[73,78,86,65,76,73,68,32,68,73,82,69,67,84,79,82,89,0],"graphics":[],"sourceName":"/src/utils/cd.asm","debugInfo":{"debugStrings":[],"assemblyFile":"/src/utils/cd.asm","assemblySource":".data\n invalid_dir: \"INVALID DIRECTORY\"\n\n.text\nmain:\n sys chdir\n eqi rv 0\n jnz done\n \n li a0 invalid_dir\n li a1 256\n sys wstr\n \n done:\n sys exit\n\n","assemblyLineNumbers":[6,6,7,7,8,8,10,10,10,10,11,11,11,11,12,12,15,15],"originalFile":"","originalLineNumbers":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"globals":[],"locals":[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]}} -------------------------------------------------------------------------------- /src/main/resources/public/img/contract.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionTypeError.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.ParseException; 4 | 5 | public final class ExpressionTypeError extends Expression implements Error { 6 | public final Expression inner; 7 | public final ParseException exception; 8 | 9 | public ExpressionTypeError(Expression inner, String message) { 10 | super(inner.start, inner.end, inner.type()); 11 | this.inner = inner; 12 | this.exception = new ParseException(new ParseException.Message(inner.span(), message)); 13 | } 14 | 15 | 16 | @Override 17 | public ParseException exception() { 18 | return exception; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/WebCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.emulator.MonTanaMiniComputer; 4 | import mtmc.os.shell.ShellCommand; 5 | import mtmc.tokenizer.MTMCTokenizer; 6 | import mtmc.web.WebServer; 7 | 8 | import java.awt.*; 9 | 10 | public class WebCommand extends ShellCommand { 11 | @Override 12 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) throws Exception{ 13 | WebServer server = WebServer.getInstance(computer); 14 | Desktop.getDesktop().browse(server.getURL()); 15 | } 16 | 17 | @Override 18 | public String getHelp() { 19 | return "web - starts the web UI"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/* 8 | *.iws 9 | *.iml 10 | *.ipr 11 | 12 | ### Eclipse ### 13 | .apt_generated 14 | .classpath 15 | .factorypath 16 | .project 17 | .settings 18 | .springBeans 19 | .sts4-cache 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ 27 | build/ 28 | !**/src/main/**/build/ 29 | !**/src/test/**/build/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | 34 | ### Mac OS ### 35 | .DS_Store 36 | 37 | ### Disk ### 38 | disk/usr/ 39 | disk/logs/ 40 | # TODO: remove other disk entires so student's don't commit them! -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/DeclarationTypedef.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | public final class DeclarationTypedef extends Declaration implements TypeDeclaration { 7 | public final TypeExpr type; 8 | public final Token name; 9 | 10 | 11 | public DeclarationTypedef(Token start, TypeExpr type, Token name, Token end) { 12 | super(start, type.end); 13 | this.type = type; 14 | this.name = name; 15 | } 16 | 17 | @Override 18 | public String name() { 19 | return name.content(); 20 | } 21 | 22 | @Override 23 | public SeaType type() { 24 | return type.type(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/Unit.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Symbol; 4 | import mtmc.lang.sea.Token; 5 | 6 | import java.util.LinkedHashMap; 7 | import java.util.List; 8 | 9 | public final class Unit extends Ast { 10 | public final String source; 11 | public final String filename; 12 | public final List declarations; 13 | public final LinkedHashMap symbols; 14 | 15 | public Unit(String filename, String source, List declarations, LinkedHashMap globals) { 16 | super(Token.SOF, Token.EOF); 17 | this.source = source; 18 | this.filename = filename; 19 | this.declarations = declarations; 20 | this.symbols = globals; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/StatementFor.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.Token; 4 | 5 | public final class StatementFor extends Statement { 6 | public final Expression initExpression; 7 | public final StatementVar initStatement; 8 | public final Expression condition; 9 | public final Expression inc; 10 | public final Statement body; 11 | 12 | public StatementFor(Token start, Expression initExpression, StatementVar initStatement, Expression condition, Expression inc, Statement body) { 13 | super(start, body.end); 14 | this.initExpression = initExpression; 15 | this.initStatement = initStatement; 16 | this.condition = condition; 17 | this.inc = inc; 18 | this.body = body; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/mtmc/emulator/MTMCObserver.java: -------------------------------------------------------------------------------- 1 | package mtmc.emulator; 2 | 3 | public interface MTMCObserver { 4 | 5 | void consoleUpdated(); 6 | 7 | void consolePrinting(); 8 | 9 | void executionUpdated(); 10 | 11 | void filesystemUpdated(); 12 | 13 | void registerUpdated(int register, int value); 14 | 15 | void memoryUpdated(int address, byte value); 16 | 17 | void displayUpdated(); 18 | 19 | void instructionFetched(short instruction); 20 | 21 | void beforeExecution(short instruction); 22 | 23 | void afterExecution(short instruction); 24 | 25 | void stepExecution(); 26 | 27 | void computerReset(); 28 | 29 | void requestCharacter(); 30 | 31 | void requestInteger(); 32 | 33 | void requestString(); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/resources/templates/console.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Fullscreen 4 | Restore 5 |
6 | Welcome to MtOS! Type ? for help

7 |
8 |
9 |
10 | mtmc$ 11 | 12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/fs/ExecutableFileTypeDetector.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.fs; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.spi.FileTypeDetector; 7 | import mtmc.os.exec.Executable; 8 | 9 | /** 10 | * 11 | * @author jbanes 12 | */ 13 | public class ExecutableFileTypeDetector extends FileTypeDetector { 14 | 15 | @Override 16 | public String probeContentType(Path path) throws IOException { 17 | try(var in = Files.newInputStream(path)) { 18 | if(in.read() != '{') return null; 19 | } 20 | 21 | try { 22 | Executable.load(path); 23 | 24 | return "text/mtmc16-bin"; 25 | } catch(IOException e) { 26 | return null; 27 | } 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /disk/data/cloverleaf.cells: -------------------------------------------------------------------------------- 1 | ! cloverleaf 2 | ....................................... 3 | ....................................... 4 | ....................................... 5 | ....................................... 6 | ....................................... 7 | ....................................... 8 | ....................................... 9 | ....................................... 10 | ....................................... 11 | ....................................... 12 | ....................................... 13 | ...................O.O........ 14 | .................OOO.OOO. 15 | ................O...O...O 16 | ................O.O.O.O.O 17 | .................OOOOOOO. 18 | ....................... 19 | .................OOOOOOO. 20 | ................O.O.O.O.O 21 | ................O...O...O 22 | .................OOO.OOO. 23 | ...................O.O... 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/StepCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.emulator.MonTanaMiniComputer; 4 | import mtmc.os.shell.ShellCommand; 5 | import mtmc.tokenizer.MTMCTokenizer; 6 | 7 | public class StepCommand extends ShellCommand { 8 | @Override 9 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) { 10 | switch (computer.getStatus()) { 11 | case READY -> { 12 | computer.setStatus(MonTanaMiniComputer.ComputerStatus.EXECUTING); 13 | computer.fetchAndExecute(); 14 | } 15 | case EXECUTING -> computer.fetchAndExecute(); 16 | default -> { 17 | } 18 | } 19 | } 20 | 21 | @Override 22 | public String getHelp() { 23 | return "step - runs the next instruction"; 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionInitializer.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public final class ExpressionInitializer extends Expression { 11 | public final List values; 12 | 13 | private static SeaType.Initializer blobType(List values) { 14 | var types = new ArrayList(); 15 | 16 | for (var value : values) { 17 | types.add(value.type()); 18 | } 19 | 20 | return new SeaType.Initializer(types); 21 | } 22 | 23 | public ExpressionInitializer(Token start, List values, Token end) { 24 | super(start, end, blobType(values)); 25 | this.values = List.copyOf(values); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/fs/PlainTextFileTypeDetector.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.fs; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.spi.FileTypeDetector; 7 | import mtmc.os.exec.Executable; 8 | 9 | /** 10 | * 11 | * @author jbanes 12 | */ 13 | public class PlainTextFileTypeDetector extends FileTypeDetector { 14 | 15 | @Override 16 | public String probeContentType(Path path) throws IOException { 17 | int c; 18 | 19 | try(var in = Files.newInputStream(path)) { 20 | // Detect non-ASCII chararcters 21 | while ((c = in.read()) >= 0) { 22 | if (c < 9 || c > 126) { 23 | return null; 24 | } 25 | } 26 | } 27 | 28 | return "text/plain"; 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /disk/data/r2d2.cells: -------------------------------------------------------------------------------- 1 | ! r2d2 2 | ........................................ 3 | ........................................ 4 | ........................................ 5 | ........................................ 6 | ........................................ 7 | ........................................ 8 | ........................................ 9 | ........................................ 10 | ........................................ 11 | ........................................ 12 | ........................................ 13 | ........................................ 14 | ........................................ 15 | ..................OO........ 16 | ..................OO... 17 | ....................... 18 | ................OOOOOO. 19 | ...............O......O 20 | ...............OOO...OO 21 | ....................... 22 | ...............OOOOOOOO 23 | ...............O......O 24 | ..................OO... 25 | ..................OO... 26 | -------------------------------------------------------------------------------- /disk/data/boss.cells: -------------------------------------------------------------------------------- 1 | !boss 2 | ....................................... 3 | ....................................... 4 | ....................................... 5 | ....................................... 6 | ....................................... 7 | ....................................... 8 | ....................................... 9 | ....................................... 10 | ....................................... 11 | ....................................... 12 | ....................................... 13 | ...................O.......... 14 | ..................O.O.... 15 | ..................O.O.... 16 | .................OO.OO... 17 | ................O.....O.. 18 | ...............O.O.O.O.O. 19 | ...............O.O...O.O. 20 | ..............OO.O...O.OO 21 | ..............O..O.O.O..O 22 | ................O.....O.. 23 | .................OO.OO... 24 | ..................O.O.... 25 | ..................O.O.... 26 | ...................O..... 27 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/Expression.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | import java.util.Objects; 7 | 8 | public sealed abstract class Expression extends Ast permits ExpressionAccess, ExpressionBin, ExpressionCall, ExpressionCast, ExpressionChar, ExpressionIdent, ExpressionIndex, ExpressionInitializer, ExpressionInteger, ExpressionParens, ExpressionPostfix, ExpressionPrefix, ExpressionString, ExpressionSyntaxError, ExpressionTernary, ExpressionTypeError { 9 | private final SeaType type; 10 | public Expression(Token start, Token end, SeaType type) { 11 | super(start, end); 12 | this.type = Objects.requireNonNull(type, "'type' cannot be null"); 13 | } 14 | 15 | public SeaType type() { 16 | return type; 17 | } 18 | 19 | public enum ValueKind { 20 | Addressable, 21 | Immediate 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /disk/data/airforce.cells: -------------------------------------------------------------------------------- 1 | ! airforce 2 | ....................................... 3 | ....................................... 4 | ....................................... 5 | ....................................... 6 | ....................................... 7 | ....................................... 8 | ....................................... 9 | ....................................... 10 | ....................................... 11 | ....................................... 12 | ....................O........... 13 | ...................O.O..... 14 | ....................O...... 15 | ........................... 16 | ..................OOOOO.... 17 | .................O.....O.OO 18 | ................O.OO...O.OO 19 | ................O.O..O.O... 20 | .............OO.O...OO.O... 21 | .............OO.O.....O.... 22 | .................OOOOO..... 23 | ........................... 24 | ...................O....... 25 | ..................O.O...... 26 | ...................O....... 27 | -------------------------------------------------------------------------------- /disk/data/clock.cells: -------------------------------------------------------------------------------- 1 | ! clock 2 | ! Tick-tocks back and forth 3 | ....................................... 4 | ....................................... 5 | ....................................... 6 | ....................................... 7 | ....................................... 8 | ....................................... 9 | ....................................... 10 | ....................................... 11 | ....................................... 12 | ....................................... 13 | ....................................... 14 | ....................................... 15 | .............O...... 16 | ...........OO.. 17 | .............OO 18 | ............O.. 19 | ....................................... 20 | ....................................... 21 | ....................................... 22 | ....................................... 23 | .......................O...... 24 | .......................O.O 25 | ......................O O 26 | ........................O -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/ExpressionSyntaxError.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.ParseException; 4 | import mtmc.lang.sea.SeaType; 5 | import mtmc.lang.sea.Token; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public final class ExpressionSyntaxError extends Expression implements SyntaxError { 9 | @Nullable 10 | public final Expression child; 11 | public final ParseException exception; 12 | 13 | public ExpressionSyntaxError(Token token, String message) { 14 | this(null, token, message); 15 | } 16 | 17 | public ExpressionSyntaxError(@Nullable Expression child, Token token, String message) { 18 | super(child == null ? token : child.start, token, SeaType.INT); 19 | this.child = child; 20 | this.exception = new ParseException(new ParseException.Message(token, message)); 21 | } 22 | 23 | @Override 24 | public ParseException exception() { 25 | return exception; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /disk/data/gourmet.cells: -------------------------------------------------------------------------------- 1 | ! gourmet 2 | ............................. 3 | ............................. 4 | ............................. 5 | ............................. 6 | ............................. 7 | ............................. 8 | ............................. 9 | ............................. 10 | ...................OO........ 11 | ...................O......... 12 | ...........O.OO.OO.O.....OO.. 13 | ...........OO.O.O.O......O... 14 | .................O........O.. 15 | .........................OO.. 16 | ............................. 17 | ....................O....OO.. 18 | .........O.........O.O..O.O.. 19 | .........OOO......OO.OO..O... 20 | ............O.............OOO 21 | ...........O.O..............O 22 | ...........OO................ 23 | ............................. 24 | ...........OO................ 25 | ...........O........O........ 26 | ............O......O.O.O.OO.. 27 | ...........OO.....O.OO.OO.O.. 28 | ..................O.......... 29 | .................OO.......... 30 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/Statement.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.ParseException; 4 | import mtmc.lang.sea.Token; 5 | 6 | public abstract sealed class Statement extends Ast permits StatementBlock, StatementBreak, StatementContinue, StatementDoWhile, StatementExpression, StatementFor, StatementGoto, StatementIf, StatementReturn, StatementSyntaxError, StatementVar, StatementWhile 7 | { 8 | private Token labelAnchor = null; 9 | 10 | public Statement(Token start, Token end) { 11 | super(start, end); 12 | } 13 | 14 | public void setLabelAnchor(Token labelAnchor) throws ParseException { 15 | if (labelAnchor == null) return; 16 | if (this.labelAnchor != null) { 17 | throw new ParseException(new ParseException.Message(labelAnchor, "this statement has been labeled twice!!")); 18 | } 19 | this.labelAnchor = labelAnchor; 20 | } 21 | 22 | public Token getLabelAnchor() { 23 | return labelAnchor; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/mtmc/lang/sea/Util.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea; 2 | 3 | import mtmc.lang.ParseException; 4 | 5 | public class Util { 6 | public static void reportError(String src, StringBuilder sb, ParseException e) { 7 | sb.append("Error:\n"); 8 | for (var msg : e.messages) { 9 | var lo = Token.getLineAndOffset(src, msg.start().start()); 10 | int lineNo = lo[0]; 11 | int column = lo[1]; 12 | var line = Token.getLineFor(src, msg.start().start()); 13 | String prefix = " %03d:%03d | ".formatted(lineNo, column); 14 | String info = " ".repeat(prefix.length() - 2) + "| "; 15 | sb.append(info).append(msg.message()).append('\n'); 16 | sb.append(prefix).append(line).append('\n'); 17 | sb 18 | .repeat(' ', prefix.length() + column - 1) 19 | .repeat('^', Math.max(1, msg.end().end() - msg.start().start())); 20 | sb.append("\n\n"); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/mtmc/util/StringEscapeUtils.java: -------------------------------------------------------------------------------- 1 | package mtmc.util; 2 | 3 | public final class StringEscapeUtils { 4 | private StringEscapeUtils() { 5 | } 6 | 7 | public static String escapeString(String s) { 8 | s = s 9 | .replace("\"", "\\\"") 10 | .replace("\\", "\\\\") 11 | .replace("\b", "\\b") 12 | .replace("\n", "\\n") 13 | .replace("\t", "\\t") 14 | .replace("\f", "\\f") 15 | .replace("\r", "\\r"); 16 | 17 | var out = new StringBuilder(); 18 | out.append('"'); 19 | s.codePoints() 20 | .forEach(c -> { 21 | if (c < 32 || c > 0x7f) { 22 | out.append("\\u{").append(Integer.toHexString(c)).append("}"); 23 | } else { 24 | out.append((char) c); 25 | } 26 | }); 27 | out.append('"'); 28 | return out.toString(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/mtmc/emulator/MTMCIO.java: -------------------------------------------------------------------------------- 1 | package mtmc.emulator; 2 | 3 | public class MTMCIO { 4 | 5 | int value = 0; 6 | 7 | enum Buttons{ 8 | UP(0b1000_0000), 9 | DOWN(0b0100_0000), 10 | LEFT(0b0010_0000), 11 | RIGHT(0b0001_0000), 12 | START(0b0000_1000), 13 | SELECT(0b0000_0100), 14 | B(0b0000_0010), 15 | A(0b0000_0001); 16 | 17 | private final int mask; 18 | Buttons(int mask) { 19 | this.mask = mask; 20 | } 21 | } 22 | 23 | public void keyPressed(String key) { 24 | Buttons button = Buttons.valueOf(key.toUpperCase()); 25 | value = value | button.mask; 26 | } 27 | 28 | public void keyReleased(String key) { 29 | Buttons button = Buttons.valueOf(key.toUpperCase()); 30 | value = value & ~button.mask; 31 | } 32 | 33 | public int getValue() { 34 | return value; 35 | } 36 | 37 | public void setValue(int value) { 38 | this.value = value; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/resources/templates/editors/image.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
{{computer.currentFilename}}
5 |
6 |
7 | 8 | 9 |
10 | 11 |
12 |
13 |
14 | {{computer.currentFilename}} 15 |
16 |
-------------------------------------------------------------------------------- /src/main/resources/templates/display.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | the display 5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 | 26 | 27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /disk/data/sparky.cells: -------------------------------------------------------------------------------- 1 | ! sparky 2 | ! Cool spaceship that flies to the left 3 | ........................................ 4 | ........................................ 5 | ........................................ 6 | ........................................ 7 | ........................................ 8 | ........................................ 9 | ........................................ 10 | ........................................ 11 | ........................................ 12 | ........................................ 13 | ........................................ 14 | ...................O.................... 15 | ...................O...............OO... 16 | ...............OO.O.OOO..........OO...O. 17 | .........O.OO.OO.OO..O.O...OO.OOOO...... 18 | .........O...OO..O.OO..OOO..O.OO..OO...O 19 | .........O.OO....OOO.O.OOO......OO..O... 20 | .................OO.O...............O..O 21 | .........O.OO....OOO.O.OOO......OO..O... 22 | .........O...OO..O.OO..OOO..O.OO..OO...O 23 | .........O.OO.OO.OO..O.O...OO.OOOO...... 24 | ...............OO.O.OOO..........OO...O. 25 | ...................O...............OO... 26 | ...................O.................... 27 | -------------------------------------------------------------------------------- /disk/src/games/dino/dino.asm: -------------------------------------------------------------------------------- 1 | .text 2 | main: 3 | li sp, 0x8000 # Set up stack pointer if required by your system 4 | 5 | li a0, rex_buffer # destination buffer 6 | li a1, 1024 # max bytes to read 7 | li a2, rex_path # path to rex.png 8 | sys 0x10 # rfile syscall 9 | 10 | teq rv, 0 11 | jne file_error 12 | 13 | li t0, 78 # rex x position (col) 14 | li t1, 68 # rex y position (row) 15 | 16 | loop: 17 | sys fbreset # clear the screen 18 | 19 | mov a0, rex_buffer # pointer to image data 20 | mov a1, t0 # x position 21 | mov a2, t1 # y position 22 | sys 0x50 # drawimg 23 | 24 | sys fbflush # update screen 25 | 26 | li a0, 100 # sleep 100 ms 27 | sys sleep 28 | 29 | j loop # loop forever 30 | 31 | file_error: 32 | li a0, err_msg 33 | sys 0x06 # wstr 34 | sys 0x00 # exit 35 | 36 | rex_path: 37 | .string "src/games/dino/rex.png" 38 | 39 | err_msg: 40 | .string "Error loading rex.png\n" 41 | 42 | rex_buffer: 43 | .space 1024 # buffer for image 44 | -------------------------------------------------------------------------------- /disk/data/brain.cells: -------------------------------------------------------------------------------- 1 | ! brain 2 | ! A structure that "swims" from the 3 | ! right of the screen to the left 4 | ....................................... 5 | ....................................... 6 | ....................................... 7 | ....................................... 8 | ....................................... 9 | ....................................... 10 | ....................................... 11 | .............................OO........ 12 | ............................O..O.....OO 13 | ............................OOO...OOO.. 14 | ............................O..O.OOOO.. 15 | .............................OOO...O... 16 | .............................O..OOO.... 17 | ...............................O....OO. 18 | ...............................OOOOO.O. 19 | ....................................... 20 | ...............................OOOOO.O. 21 | ...............................O....OO. 22 | .............................O..OOO.... 23 | .............................OOO...O... 24 | ............................O..O.OOOO.. 25 | ............................OOO...OOO.. 26 | ............................O..O.....OO 27 | .............................OO........ 28 | -------------------------------------------------------------------------------- /disk/data/galaxy.cells: -------------------------------------------------------------------------------- 1 | ! galaxy 2 | ! Kok's galaxy - a whirling oscillator 3 | ....................................... 4 | ....................................... 5 | ....................................... 6 | ....................................... 7 | ....................................... 8 | ....................................... 9 | ....................................... 10 | ....................................... 11 | ....................................... 12 | ....................................... 13 | ....................................... 14 | ....................................... 15 | ....................................... 16 | ...............OOOOOO.OO..... 17 | ...............OOOOOO.OO 18 | ......................OO 19 | ...............OO.....OO 20 | ...............OO.....OO 21 | ...............OO.....OO 22 | ...............OO....... 23 | ...............OO.OOOOOO 24 | ...............OO.OOOOOO....................................... 25 | ....................................... 26 | ....................................... 27 | ....................................... 28 | ....................................... 29 | ....................................... 30 | -------------------------------------------------------------------------------- /disk/data/queenbee.cells: -------------------------------------------------------------------------------- 1 | ! queenbee 2 | ........................................ 3 | ........................................ 4 | ........................................ 5 | ........................................ 6 | ........................................ 7 | ........................................ 8 | ........................................ 9 | ........................................ 10 | ........................................ 11 | ........................................ 12 | ........................................ 13 | ........................................ 14 | ........................................ 15 | ........................................ 16 | ..................O................. 17 | ..................OOOO........ 18 | ........OO.........OOOO.....OO 19 | ........OO.........O..O.....OO 20 | ...................OOOO....... 21 | ..................OOOO........ 22 | ..................O........... 23 | ........................................ 24 | ........................................ 25 | ........................................ 26 | ........................................ 27 | ........................................ 28 | ........................................ -------------------------------------------------------------------------------- /disk/src/games/ant.asm: -------------------------------------------------------------------------------- 1 | .text 2 | main: 3 | li t0 78 # ant col 4 | li t1 68 # ant row 5 | 6 | loop: 7 | 8 | # get the latest IO 9 | sys joystick 10 | mov t2 rv 11 | 12 | mov t3 t2 13 | andi t3 0b1000_0000 # up 14 | jz l1 15 | dec t1 4 16 | l1: 17 | 18 | mov t3 t2 19 | andi t3 0b0100_0000 # down 20 | jz l2 21 | inc t1 4 22 | l2: 23 | 24 | mov t3 t2 25 | andi t3 0b0010_0000 # left 26 | jz l3 27 | dec t0 4 28 | l3: 29 | 30 | mov t3 t2 31 | andi t3 0b0001_0000 # right 32 | jz l4 33 | inc t0 4 34 | l4: 35 | 36 | lti t0 0 37 | jz l5 38 | li t0 159 39 | l5: 40 | 41 | lti t1 0 42 | jz l6 43 | li t1 139 44 | l6: 45 | 46 | modi t0 160 47 | modi t1 144 48 | 49 | sys fbreset # reset the frame buffer 50 | 51 | mov a0 t0 # move x and y into args 52 | mov a1 t1 53 | li a2 4 # 4x4 rectangle 54 | li a3 4 55 | sys fbrect # draw the ant 56 | 57 | sys fbflush # sync the screen 58 | 59 | li a0 100 60 | sys sleep # sleep 100 millis 61 | 62 | j loop # do it all again 63 | -------------------------------------------------------------------------------- /disk/data/quad.cells: -------------------------------------------------------------------------------- 1 | ! quad 2 | ! Similar to the barber pole, except 3 | ! the dots oscillate around 4 points 4 | ! instead of two 5 | ........................................ 6 | ........................................ 7 | ........................................ 8 | ........................................ 9 | ........................................ 10 | ........................................ 11 | ........................................ 12 | ........................................ 13 | ........................................ 14 | ........................................ 15 | ........................................ 16 | ........................................ 17 | ........................................ 18 | ........................................ 19 | ........................................ 20 | .................OO..OO..... 21 | .................O..O.O 22 | ..................O.... 23 | .....................O. 24 | .................O.O..O 25 | .................OO..OO 26 | ........................................ 27 | ........................................ 28 | ........................................ 29 | ........................................ 30 | ........................................ -------------------------------------------------------------------------------- /disk/src/sort.asm: -------------------------------------------------------------------------------- 1 | .data: 2 | NEW_LINE: 10 3 | SPACE: 32 4 | NULL: 0 5 | sp_address: NA 6 | .text 7 | main: 8 | sw t5 a0 9 | main_loop: 10 | la a0 t5 # Loads current address (Each iteration) 11 | lbo t0 a0 0 #Loads byte into t0(Starts with first char address) 12 | neqi t0 SPACE 13 | jz increment # If test-bit is 0 (value is a SPACE), increment 14 | neqi t0 NULL # If null, end. 15 | jz end 16 | 17 | jal parse_and_push 18 | jal increment 19 | jal main_loop 20 | end: 21 | sort: 22 | TODO 23 | print: 24 | # New line - Print Value - Decrement - Need to add null terminator 25 | li a0 NEW_LINE 26 | sys wchr 27 | li a0 sp_address 28 | sys wint 29 | dec sp_address 30 | jal print 31 | sys exit 32 | 33 | parse_and_push: 34 | #la a0 t5 # Loads current register address(t5) into a0 35 | sys atoi # Determines if value in register is a short, stores value into rv,else returns 0 36 | push rv # Pushes onto stack 37 | ret 38 | 39 | increment: 40 | inc t5 41 | jal main_loop 42 | 43 | #Have a counter that iterates through and prints list with "\n" -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/ASMElement.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm; 2 | 3 | import mtmc.tokenizer.MTMCToken; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public abstract class ASMElement implements HasLocation { 9 | 10 | private final List labels; 11 | List errors = new ArrayList<>(); 12 | private int location = -1; 13 | protected int lineNumber; 14 | 15 | public ASMElement(List labels, int lineNumber) { 16 | this.labels = labels; 17 | this.lineNumber = lineNumber; 18 | } 19 | 20 | public List getLabels() { 21 | return labels; 22 | } 23 | 24 | public int getLocation() { 25 | return location; 26 | } 27 | 28 | public void setLocation(int location) { 29 | this.location = location; 30 | } 31 | 32 | public List getErrors() { 33 | return errors; 34 | } 35 | 36 | public void addError(MTMCToken token, String error) { 37 | errors.add(new ASMError(token, error)); 38 | } 39 | 40 | abstract public void addError(String integerValueRequired); 41 | 42 | public int getLineNumber() { 43 | return lineNumber; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /disk/data/blinkership.cells: -------------------------------------------------------------------------------- 1 | ! blinkership 2 | ! An object which travels while 3 | ! growing larger by leaving an 4 | ! increasing trail of blinkers, 5 | ! but which leaves no permanent 6 | ! exhaust. 7 | ....................................... 8 | ....................................... 9 | ....................................... 10 | ....................................... 11 | ....................................... 12 | ....................................... 13 | ....................................... 14 | ....................................... 15 | ..................O..O............... 16 | .................O................... 17 | .................O...O............... 18 | .........OO......OOOO................ 19 | ........OOOO......................... 20 | .......OO.OO......................... 21 | ........OO.....OO.OOO................ 22 | ..............O.....OO.......O....OOO 23 | .............OO.......O......O....O.O 24 | ..............O.....OO.......O....OOO 25 | ........OO.....OO.OOO................ 26 | .......OO.OO......................... 27 | ........OOOO......................... 28 | .........OO......OOOO................ 29 | .................O...O............... 30 | .................O................... 31 | ..................O..O............... -------------------------------------------------------------------------------- /disk/data/hertz.cells: -------------------------------------------------------------------------------- 1 | ! hertz 2 | ! "Hertz oscillator" - A period 8 oscillator 3 | ! found by John Conway's group in 1970 4 | ....................................... 5 | ....................................... 6 | ....................................... 7 | ....................................... 8 | ....................................... 9 | ....................................... 10 | ....................................... 11 | ....................................... 12 | ....................................... 13 | ....................................... 14 | ....................................... 15 | ....................................... 16 | ....................................... 17 | ..................OO.O................. 18 | ..................O.OO................. 19 | ....................................... 20 | ...................OOO................. 21 | ..................O.O.O.OO............. 22 | ..................O...O.OO............. 23 | ...............OO.O...O................ 24 | ...............OO.O...O................ 25 | ...................OOO................. 26 | ....................................... 27 | ...................OO.O................ 28 | ...................O.OO................ 29 | ....................................... 30 | ....................................... -------------------------------------------------------------------------------- /disk/data/101.cells: -------------------------------------------------------------------------------- 1 | ! 101 2 | ! Found by Achim Flammenkamp in August 3 | ! 1994. The name was suggested by Bill 4 | ! Gosper, noting that the phase shown 5 | ! below displays the period in binary. 6 | ....................................... 7 | ....................................... 8 | ....................................... 9 | ....................................... 10 | ....................................... 11 | ....................................... 12 | ....................................... 13 | ....................................... 14 | ....................................... 15 | ....................................... 16 | ....................................... 17 | ....................................... 18 | ..............OO......OO............... 19 | .............O.O......O.O.............. 20 | .............O..........O.............. 21 | ..........OO.O..........O.OO........... 22 | ..........OO.O.O..OO..O.O.OO........... 23 | .............O.O.O..O.O.O.............. 24 | .............O.O.O..O.O.O.............. 25 | ..........OO.O.O..OO..O.O.OO........... 26 | ..........OO.O..........O.OO........... 27 | .............O..........O.............. 28 | .............O.O......O.O.............. 29 | ..............OO......OO............... 30 | ....................................... 31 | ....................................... -------------------------------------------------------------------------------- /src/main/resources/templates/editors/monaco.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |
{{computer.currentFilename}}
7 |
8 |
9 | 10 | 11 |
12 | 13 |
14 |
15 |
16 |
17 | 18 |
19 |
-------------------------------------------------------------------------------- /src/main/java/mtmc/asm/instructions/JumpRegisterInstruction.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.instructions; 2 | 3 | import mtmc.asm.Assembler; 4 | import mtmc.emulator.Register; 5 | import mtmc.tokenizer.MTMCToken; 6 | 7 | import java.util.List; 8 | 9 | import static mtmc.util.BinaryUtils.getBits; 10 | 11 | public class JumpRegisterInstruction extends Instruction { 12 | 13 | private MTMCToken register; 14 | 15 | public JumpRegisterInstruction(InstructionType type, List label, MTMCToken instructionToken) { 16 | super(type, label, instructionToken); 17 | } 18 | 19 | public void setRegister(MTMCToken register) { 20 | this.register = register; 21 | } 22 | 23 | @Override 24 | public void genCode(byte[] output, Assembler assembler) { 25 | int opcode = 0b1001; 26 | output[getLocation()] = (byte) (opcode << 4); 27 | int reg = Register.toInteger(register.stringValue()); 28 | output[getLocation()+1] = (byte) reg; 29 | } 30 | 31 | public static String disassemble(short instruction) { 32 | if (getBits(16, 5, instruction) == 0b1001) { 33 | short reg = getBits(4, 4, instruction); 34 | StringBuilder sb = new StringBuilder("jr"); 35 | sb.append(Register.fromInteger(reg)); 36 | } 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /disk/data/twogun.cells: -------------------------------------------------------------------------------- 1 | ! twogun 2 | ! V. Everett Boyer and Doug Petrie 3 | ! At one point this was the smallest known period-60 gun. 4 | ! It uses two copies of the Gosper glider gun. 5 | ...........................O........... 6 | .........................O.O........... 7 | ...............OO......OO............OO 8 | ..............O...O....OO............OO 9 | ...OO........O.....O...OO.............. 10 | ...OO........O...O.OO....O.O........... 11 | .............O.....O.......O........... 12 | ..............O...O.................... 13 | ...............OO...................... 14 | ..........................O............ 15 | ...........................OO.......... 16 | ..........................OO........... 17 | ....................................... 18 | ....................................... 19 | ....................................... 20 | .....................OO................ 21 | .........O.O..........OO............... 22 | .........O..O........O................. 23 | OO..........OO...........OO............ 24 | OO........O...OO........O.O............ 25 | .....OO.....OO.........O......OO....... 26 | ....O....O..O..........O..O..O..O...... 27 | .........O.O...........O......OOO...... 28 | ........................O.O.....OOO.... 29 | .........................OO......O.O... 30 | ...................................O... 31 | ...................................OO.. -------------------------------------------------------------------------------- /disk/data/barberpole.cells: -------------------------------------------------------------------------------- 1 | ! barberpole 2 | ! A section of an oscillator or 3 | ! spaceship in which each generation of 4 | ! the section looks the same, except 5 | ! for a sideways shift. 6 | ....................................... 7 | ....................................... 8 | ....................................... 9 | ....................................... 10 | ....................................... 11 | ....................................... 12 | ....................................... 13 | ....................................... 14 | ...........................OO..... 15 | ............................O 16 | .........................O.O. 17 | ............... 18 | .......................O.O. 19 | ............... 20 | .....................O.O. 21 | ............... 22 | ...................O.O. 23 | ............... 24 | .................O.O. 25 | ............... 26 | ...............O.O. 27 | ............... 28 | .............O.O. 29 | ............... 30 | ...........O.O. 31 | ............... 32 | .........O.O... 33 | ........O...... 34 | ........OO..... 35 | ....................................... 36 | ....................................... 37 | ....................................... 38 | ....................................... 39 | ....................................... 40 | ....................................... 41 | ....................................... 42 | ....................................... 43 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/instructions/ALUOp.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.instructions; 2 | 3 | public enum ALUOp { 4 | ADD(0x0000, false), 5 | SUB(0x0001, false), 6 | MUL(0x0002, false), 7 | DIV(0x0003, false), 8 | MOD(0x0004, false), 9 | AND(0x0005, false), 10 | OR(0x0006, false), 11 | XOR(0x0007, false), 12 | SHL(0x0008, false), 13 | SHR(0x0009, false), 14 | MIN(0x000A, false), 15 | MAX(0x000B, false), 16 | NOT(0x000C, true), 17 | LNOT(0x000D, true), 18 | NEG(0x000E, true), 19 | IMM(0x000F, true); 20 | 21 | int opCode; 22 | boolean unary; 23 | 24 | ALUOp(int value, boolean unary) { 25 | this.opCode = value; 26 | this.unary = unary; 27 | } 28 | 29 | public static int toInteger(String instruction) { 30 | return valueOf(instruction.toUpperCase()).opCode; 31 | } 32 | 33 | public static String fromInt(short opCode) { 34 | return values()[opCode].name().toLowerCase(); 35 | } 36 | 37 | public static boolean isALUOp(String op) { 38 | try { 39 | ALUOp aluOp = ALUOp.valueOf(op.toUpperCase()); 40 | return true; 41 | } catch (IllegalArgumentException e) { 42 | return false; 43 | } 44 | } 45 | 46 | public int getOpCode() { 47 | return opCode; 48 | } 49 | 50 | public boolean isUnary() { 51 | return unary; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/resources/templates/newdir.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | Exploring - {{ computer.getFileSystem().getCWD() }} 5 |
6 | 7 |
8 |
Create New Directory
9 |
10 |
11 |
12 | 14 |
15 |
16 |
17 | Name: 18 |
19 |
20 | 21 |
22 |
23 | 24 | 25 |
26 |
{{computer.currentError}}
27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /disk/data/beacons.cells: -------------------------------------------------------------------------------- 1 | ! beacons 2 | ! Two big beacons 3 | ....................................... 4 | ....................................... 5 | ....................................... 6 | ....................................... 7 | ....................................... 8 | ....................................... 9 | ....................................... 10 | ....................................... 11 | ....................................... 12 | ....................................... 13 | ........................OOO............ 14 | ........................OOO............ 15 | ........................OOO............ 16 | .....................OOO............... 17 | .....................OOO............... 18 | .....................OOO............... 19 | ....................................... 20 | ....................................... 21 | ....................................... 22 | ....................................... 23 | ....................................... 24 | ....................................... 25 | ...........OOO......................... 26 | ...........OOO......................... 27 | ...........OOO......................... 28 | ..............OOO...................... 29 | ..............OOO...................... 30 | ..............OOO...................... 31 | ....................................... 32 | ....................................... 33 | ....................................... 34 | ....................................... -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/data/Data.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.data; 2 | 3 | import mtmc.asm.ASMElement; 4 | import mtmc.asm.Assembler; 5 | import mtmc.tokenizer.MTMCToken; 6 | 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | public class Data extends ASMElement { 11 | public MTMCToken valueToken; 12 | private byte[] value; 13 | 14 | public Data(List labels, int lineNumber) { 15 | super(labels, lineNumber); 16 | } 17 | 18 | 19 | @Override 20 | public int getSizeInBytes() { 21 | if (value == null) { 22 | return 0; 23 | } else { 24 | return value.length; 25 | } 26 | } 27 | 28 | public void genData(byte[] dataBytes, Assembler assembler) { 29 | int offset = getLocation() - assembler.getInstructionsSizeInBytes(); 30 | for (int i = 0; i < getSizeInBytes(); i++) { 31 | byte dataByte = value[i]; 32 | dataBytes[offset + i] = dataByte; 33 | } 34 | } 35 | 36 | public void setValue(MTMCToken src, byte[] value) { 37 | this.valueToken = src; 38 | this.value = value; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "Data{" + 44 | "value=" + Arrays.toString(value) + 45 | '}'; 46 | } 47 | 48 | @Override 49 | public void addError(String err) { 50 | addError(getLabels().getLast(), err); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/ParseException.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | public class ParseException extends Exception { 9 | public final List messages; 10 | 11 | public ParseException(Message message, Message ...rest) { 12 | var messages = new ArrayList(1 + rest.length); 13 | messages.add(message); 14 | messages.addAll(Arrays.asList(rest)); 15 | this.messages = Collections.unmodifiableList(messages); 16 | } 17 | 18 | public ParseException(ParseException parent, Message message, Message ...rest) { 19 | var messages = new ArrayList( 1 + rest.length + parent.messages.size()); 20 | messages.add(message); 21 | messages.addAll(Arrays.asList(rest)); 22 | messages.addAll(parent.messages); 23 | this.messages = Collections.unmodifiableList(messages); 24 | } 25 | 26 | public record Message(Span span, String message) { 27 | public Message(Token token, String message) { 28 | this(Span.of(token), message); 29 | } 30 | 31 | public Token start() { 32 | return span.start(); 33 | } 34 | 35 | public Token end() { 36 | return span.end(); 37 | } 38 | } 39 | 40 | public String report(String source) { 41 | return "TODO: I ain't no snitch"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/mtmc/os/fs/ListingTest.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.fs; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import static org.junit.jupiter.api.Assertions.*; 5 | 6 | /** 7 | * 8 | * @author jbanes 9 | */ 10 | public class ListingTest 11 | { 12 | @Test 13 | public void testRoot() 14 | { 15 | FileSystem fs = new FileSystem(); 16 | Listing root = fs.listRoot(); 17 | 18 | assertEquals("/", root.name); 19 | assertEquals("/", root.path); 20 | assertTrue(root.directory); 21 | 22 | assertEquals("bin", root.list().get(0).name); 23 | assertEquals("data", root.list().get(1).name); 24 | assertEquals("home", root.list().get(2).name); 25 | assertEquals("img", root.list().get(3).name); 26 | assertEquals("logs", root.list().get(4).name); 27 | assertEquals("src", root.list().get(5).name); 28 | } 29 | 30 | @Test 31 | public void testCWD() 32 | { 33 | FileSystem fs = new FileSystem(); 34 | Listing cwd = fs.listCWD(); 35 | 36 | assertEquals("home", cwd.name); 37 | assertEquals("/home", cwd.path); 38 | } 39 | 40 | @Test 41 | public void testRelative() 42 | { 43 | FileSystem fs = new FileSystem(); 44 | Listing cwd = fs.listFiles("../img"); 45 | 46 | assertEquals("img", cwd.name); 47 | assertEquals("/img", cwd.path); 48 | assertEquals(4, cwd.list().size()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/exec/Executable.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.exec; 2 | 3 | import com.google.gson.Gson; 4 | import mtmc.emulator.DebugInfo; 5 | 6 | import java.io.*; 7 | import java.nio.file.Path; 8 | 9 | public record Executable( 10 | Format format, 11 | byte[] code, 12 | byte[] data, 13 | byte[][] graphics, 14 | String sourceName, 15 | DebugInfo debugInfo) { 16 | public enum Format { 17 | Orc1("orc1"); 18 | 19 | public final String name; 20 | 21 | Format(String name) { 22 | this.name = name; 23 | } 24 | } 25 | 26 | public String dump() { 27 | return new Gson().toJson(this); 28 | } 29 | 30 | public void dump(Path path) throws IOException { 31 | try (var fw = new FileWriter(path.toFile())) { 32 | dump(fw); 33 | } 34 | } 35 | 36 | public void dump(Writer writer) { 37 | var gson = new Gson(); 38 | gson.toJson(this, writer); 39 | } 40 | 41 | public static Executable load(String exe) { 42 | return new Gson().fromJson(exe, Executable.class); 43 | } 44 | 45 | public static Executable load(Path path) throws IOException { 46 | try (var fw = new FileReader(path.toFile())) { 47 | return load(fw); 48 | } 49 | } 50 | 51 | public static Executable load(Reader reader) throws IOException { 52 | var gson = new Gson(); 53 | return gson.fromJson(reader, Executable.class); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/SpeedCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import mtmc.emulator.MonTanaMiniComputer; 6 | import mtmc.os.shell.ShellCommand; 7 | import mtmc.tokenizer.MTMCTokenizer; 8 | 9 | import static mtmc.tokenizer.MTMCToken.TokenType.IDENTIFIER; 10 | import static mtmc.tokenizer.MTMCToken.TokenType.INTEGER; 11 | 12 | public class SpeedCommand extends ShellCommand { 13 | 14 | private List speeds = Arrays.asList(new Integer[] { 15 | 1, 10, 100, 1000, 10000, 100000, 1000000 16 | }); 17 | 18 | @Override 19 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) throws Exception { 20 | if (tokens.match(IDENTIFIER)) { 21 | computer.setSpeed(0); 22 | } else if (tokens.match(INTEGER)) { 23 | Integer speed = tokens.consumeAsInteger(); 24 | if (!speeds.contains(speed)) { 25 | usageException(); 26 | } 27 | computer.setSpeed(speed); 28 | } else { 29 | usageException(); 30 | } 31 | } 32 | 33 | @Override 34 | public String getHelp() { 35 | return """ 36 | speed - set the speed of the computer 37 | where is one of: 38 | raw - run with no simulated speed delay 39 | 1 - run the computer at 1hz 40 | 10 - run the computer at 10hz 41 | 100 - run the computer at 100hz 42 | 1000 - run the computer at 1khz"""; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/fs/Listing.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.fs; 2 | 3 | import java.io.File; 4 | import java.util.*; 5 | 6 | public class Listing { 7 | public final String path; 8 | public final String name; 9 | public final boolean directory; 10 | public final boolean root; 11 | 12 | private FileSystem fs; 13 | private File file; 14 | 15 | public Listing(FileSystem fs, File file) { 16 | String root = FileSystem.DISK_PATH.toFile().getAbsolutePath().replace('\\', '/'); 17 | String path = file.getAbsolutePath().substring(root.length()).replace('\\', '/'); 18 | 19 | this.fs = fs; 20 | this.file = file; 21 | 22 | this.path = path.length() > 0 ? path : "/"; 23 | this.name = path.length() > 0 ? file.getName() : "/"; 24 | this.directory = file.isDirectory(); 25 | this.root = this.path.equals("/"); 26 | } 27 | 28 | public Listing getParent() { 29 | return new Listing(fs, file.getParentFile()); 30 | } 31 | 32 | public List list() { 33 | if (!directory) { 34 | return new ArrayList<>(); 35 | } 36 | 37 | var list = new ArrayList(); 38 | var children = file.listFiles(); 39 | 40 | Arrays.sort(children, (a, b) -> { 41 | return a.getName().compareTo(b.getName()); 42 | }); 43 | 44 | for (File child : children) { 45 | list.add(new Listing(fs, child)); 46 | } 47 | 48 | return list; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/mtmc/os/fs/FileSystemTest.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.fs; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.*; 6 | 7 | public class FileSystemTest { 8 | @Test 9 | public void testPaths() { 10 | var fs = new FileSystem(); 11 | 12 | assertAll( 13 | () -> assertEquals("/", fs.resolve("/")), 14 | () -> assertEquals("/bin/bin/hello_world", fs. resolve("/bin/./bin/hello_world")), 15 | () -> assertEquals("/bin/hello_world", fs.resolve("../bin/hello_world")), 16 | () -> assertEquals("/bin/hello_world", fs.resolve("../../bin/hello_world")), 17 | () -> assertEquals("/home/hello_world", fs.resolve("hello_world")), 18 | () -> assertEquals("/home/hello_world", fs.resolve("/home/./hello_world")), 19 | () -> { 20 | fs.setCWD("/home/user/dillon/projects/testcase"); 21 | assertEquals("/home/user/dillon/projects/.local/bin", fs.resolve("hello/../../.local/bin")); 22 | }, 23 | () -> { 24 | fs.setCWD("/home/user/dillon/projects/testcase"); 25 | assertEquals("/home/user/dillon/.local/bin", fs.resolve("../../.local/bin")); 26 | }, 27 | () -> { 28 | fs.setCWD("/bin"); 29 | assertEquals("/bin/cat", fs.resolve("./cat")); 30 | assertEquals("/bin/cat", fs.resolve("cat")); 31 | } 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/DeclarationStruct.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaType; 4 | import mtmc.lang.sea.Token; 5 | 6 | import java.util.HashMap; 7 | import java.util.LinkedHashMap; 8 | import java.util.List; 9 | 10 | public final class DeclarationStruct extends Declaration implements TypeDeclaration { 11 | public final Token name; 12 | public final List fields; 13 | 14 | public DeclarationStruct(Token start, Token name, List fields, Token end) { 15 | super(start, end); 16 | this.name = name; 17 | this.fields = List.copyOf(fields); 18 | } 19 | 20 | public String name() { 21 | return name.content(); 22 | } 23 | 24 | private SeaType type; 25 | @Override 26 | public SeaType type() { 27 | if (type == null) { 28 | var fields = new LinkedHashMap(); 29 | for (var field : this.fields) { 30 | fields.put(field.name(), field.type()); 31 | } 32 | type = new SeaType.Struct(name(), fields); 33 | } 34 | return type; 35 | } 36 | 37 | public static final class Field extends Ast { 38 | public final TypeExpr type; 39 | public final Token name; 40 | 41 | public Field(TypeExpr type, Token name) { 42 | super(type.start, name); 43 | this.type = type; 44 | this.name = name; 45 | } 46 | 47 | public String name() { 48 | return name.content(); 49 | } 50 | 51 | public SeaType type() { 52 | return type.type(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/SysCall.java: -------------------------------------------------------------------------------- 1 | package mtmc.os; 2 | 3 | public enum SysCall { 4 | EXIT(0x00), 5 | RINT(0x01), 6 | WINT(0x02), 7 | RSTR(0x03), 8 | WCHR(0x04), 9 | RCHR(0x05), 10 | WSTR(0x06), 11 | PRINTF(0x07), 12 | ATOI(0x08), 13 | 14 | RFILE(0x10), 15 | WFILE(0x11), 16 | CWD(0x12), 17 | CHDIR(0x13), 18 | DIRENT(0x14), 19 | DFILE(0x15), 20 | 21 | RND(0x20), 22 | SLEEP(0x21), 23 | TIMER(0x22), 24 | 25 | FBRESET(0x30), 26 | FBSTAT(0x31), 27 | FBSET(0x32), 28 | FBLINE(0x33), 29 | FBRECT(0x34), 30 | FBFLUSH(0x35), 31 | JOYSTICK(0x3A), 32 | SCOLOR(0x3B), 33 | 34 | MEMCPY(0x40), 35 | 36 | DRAWIMG(0x50), 37 | DRAWIMGSZ(0x51), 38 | DRAWIMGCLIP(0x52), 39 | 40 | ERROR(0xFF); 41 | 42 | private final byte value; 43 | 44 | SysCall(int value) { 45 | this.value = (byte) value; 46 | } 47 | 48 | public byte getValue() { 49 | return value; 50 | } 51 | 52 | public static boolean isSysCall(String call) { 53 | try { 54 | valueOf(call.toUpperCase()); 55 | return true; 56 | } catch (IllegalArgumentException e) { 57 | return false; 58 | } 59 | } 60 | 61 | public static byte getValue(String call) { 62 | return valueOf(call.toUpperCase()).value; 63 | } 64 | 65 | public static String getString(byte syscallCode) { 66 | for (SysCall o : SysCall.values()) { 67 | if (o.getValue() == syscallCode) { 68 | return o.name().toLowerCase(); 69 | } 70 | } 71 | return null; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/GetCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.emulator.MonTanaMiniComputer; 4 | import mtmc.emulator.Register; 5 | import mtmc.os.shell.ShellCommand; 6 | import mtmc.tokenizer.MTMCToken; 7 | import mtmc.tokenizer.MTMCTokenizer; 8 | 9 | import static mtmc.tokenizer.MTMCToken.TokenType.*; 10 | 11 | public class GetCommand extends ShellCommand { 12 | 13 | @Override 14 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) throws Exception { 15 | MTMCToken memLocation = tokens.matchAndConsume(INTEGER, HEX, BINARY); 16 | if (memLocation == null) { 17 | MTMCToken register = tokens.matchAndConsume(IDENTIFIER); 18 | if (register == null) usageException(); 19 | int reg = Register.toInteger(register.stringValue()); 20 | if (reg >= 0) { 21 | computer.getConsole().println(register.stringValue() + ": " + computer.getRegisterValue(reg));; 22 | } else { 23 | throw new IllegalArgumentException("Bad register: " + register.stringValue()); 24 | } 25 | } else { 26 | if (memLocation.type() == INTEGER || memLocation.type() == BINARY || memLocation.type() == HEX) { 27 | computer.getConsole().println("MEMORY " + memLocation.intValue() + ": " + computer.fetchWordFromMemory(memLocation.intValue()));; 28 | } 29 | } 30 | } 31 | 32 | @Override 33 | public String getHelp() { 34 | return """ 35 | get - gets a memory location value 36 | loc: a register name or memory location 37 | """; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/Location.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang; 2 | 3 | public record Location(int index) { 4 | 5 | public static int[] getLineNos(String source, int... indices) { 6 | int[] out = new int[indices.length]; 7 | int index = 0, line = 1; 8 | while (index < source.length()) { 9 | for (int i = 0; i < indices.length; i++) { 10 | if (indices[i] == index) { 11 | out[i] = line; 12 | } 13 | } 14 | 15 | int cp = source.charAt(index); 16 | if (cp == '\n') { 17 | line += 1; 18 | } 19 | index += Character.charCount(cp); 20 | } 21 | return out; 22 | } 23 | 24 | public LineInfo getLineInfo(String source) { 25 | int index = 0, lineStart = 0; 26 | int lineno = 1; 27 | int column = 1; 28 | while (index < source.length()) { 29 | if (index == this.index) { 30 | break; 31 | } 32 | int cp = source.charAt(index); 33 | 34 | if (cp == '\n') { 35 | lineno += 1; 36 | lineStart = index + 1; 37 | } else { 38 | column += 1; 39 | } 40 | 41 | index += Character.charCount(cp); 42 | } 43 | 44 | while (index < source.length()) { 45 | int cp = source.charAt(index); 46 | index += Character.charCount(cp); 47 | if (cp == '\n') break; 48 | } 49 | String line = source.substring(lineStart, index); 50 | 51 | return new LineInfo(lineno, column, line); 52 | } 53 | 54 | public record LineInfo(int lineno, int column, String line) {} 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/mtmc/emulator/DebugInfo.java: -------------------------------------------------------------------------------- 1 | package mtmc.emulator; 2 | 3 | import java.util.List; 4 | import java.util.regex.Matcher; 5 | import java.util.regex.Pattern; 6 | 7 | public record DebugInfo(List debugStrings, 8 | String assemblyFile, String assemblySource, int[] assemblyLineNumbers, 9 | String originalFile, int[] originalLineNumbers, 10 | GlobalInfo[] globals, LocalInfo[][] locals) { 11 | 12 | public void handleDebugString(short debugIndex, MonTanaMiniComputer monTanaMiniComputer) { 13 | String debugString = debugStrings.get(debugIndex); 14 | Pattern compile = Pattern.compile("(\\$[a-zA-Z][a-zA-Z0-9])"); 15 | Matcher matcher = compile.matcher(debugString); 16 | StringBuilder formattedString = new StringBuilder(); 17 | int start = 0; 18 | int end; 19 | while (matcher.find()) { 20 | String match = matcher.group().substring(1); 21 | try { 22 | end = matcher.start(); 23 | formattedString.append(debugString, start, end); 24 | Register register = Register.valueOf(match.toUpperCase()); 25 | formattedString.append(monTanaMiniComputer.getRegisterValue(register)); 26 | start = matcher.end(); 27 | } catch (Exception e) { 28 | formattedString.append(match); 29 | } 30 | } 31 | formattedString.append(debugString.substring(start)); 32 | System.out.println("DEBUG[" + System.nanoTime() + "] : " + formattedString); 33 | } 34 | 35 | public record GlobalInfo(String name, int location, String type){} 36 | public record LocalInfo(String name, int offset, String type){} 37 | 38 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MTMC - MonTana state Mini Computer 2 | 3 | The MonTana state Mini Computer is a virtual computer intended to show how digital computation works in a fun and visual way. 4 | 5 | The MTSC combines ideas from the [PDP-11](https://en.wikipedia.org/wiki/PDP-11), [MIPS](https://en.wikipedia.org/wiki/MIPS_architecture), 6 | [Scott CPU](https://www.youtube.com/watch?v=RRg5hRlywIg), [Game Boy](https://en.wikipedia.org/wiki/Game_Boy) and 7 | [JVM](https://en.wikipedia.org/wiki/Java_virtual_machine) to make a relatively simple 16-bit computer that can accomplish basic computing tasks. 8 | 9 | The computer is displayed via a web interface that includes all the I/O such as console and display, visual representations of the computer state, and a built in code editor to construct and debug software for the computer. 10 | 11 | Screenshot 2025-07-14 at 2 37 33 PM 12 | 13 | 14 | ## Overall Architecture 15 | 16 | - 16-bit binary computer 17 | - byte-addressable 18 | - 2 byte (16-bit) words 19 | - 4k of Memory 20 | - 4096 bytes/addresses 21 | - 2048 words 22 | - 16 Registers (see below) 23 | - 160x144 2-bit green scale display 24 | - `00` - `#2a453b` - darkest 25 | - `01` - `#365d48` - dark 26 | - `10` - `#577c44` - light 27 | - `11` - `#7f860f` - lightest 28 | - Console for text input/output & commands 29 | - Operating System (MTOS) 30 | - Core data types are signed 16-bit integers & bytes 31 | 32 | ## Documentation 33 | 34 | - [Quick Start Guide](docs/MTMC_QUICK_START.md) 35 | - [Computer Specification](docs/MTMC_SPECIFICATION.md) 36 | - [Assembly Guide](docs/MTMC_ASSEMBLY.md) 37 | - [Frequently Asked Questions](docs/MTMC_FAQ.md) 38 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/Symbol.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea; 2 | 3 | import mtmc.lang.sea.ast.*; 4 | 5 | public class Symbol { 6 | public final String name; 7 | public final SeaType type; 8 | public final TypeDeclaration typeDecl; 9 | public final boolean isParam, isGlobal; 10 | 11 | public Symbol(DeclarationFunc.Param param) { 12 | this.name = param.name.content(); 13 | this.type = param.type.type(); 14 | this.typeDecl = null; 15 | this.isParam = true; 16 | this.isGlobal = false; 17 | } 18 | 19 | public Symbol(DeclarationVar decl) { 20 | this.name = decl.name(); 21 | this.type = decl.type.type(); 22 | this.typeDecl = null; 23 | this.isParam = false; 24 | this.isGlobal = true; 25 | } 26 | 27 | public Symbol(StatementVar stmt) { 28 | this.name = stmt.name(); 29 | this.type = stmt.type.type(); 30 | this.typeDecl = null; 31 | this.isParam = false; 32 | this.isGlobal = false; 33 | } 34 | 35 | public Symbol(DeclarationFunc func) { 36 | this.name = func.name.content(); 37 | this.type = func.type(); 38 | this.typeDecl = null; 39 | this.isParam = false; 40 | this.isGlobal = true; 41 | } 42 | 43 | public Symbol(TypeDeclaration declaration) { 44 | this.name = declaration.name(); 45 | this.type = null; 46 | this.typeDecl = declaration; 47 | this.isParam = false; 48 | this.isGlobal = false; 49 | } 50 | 51 | public boolean isAddressable() { 52 | if (this.typeDecl != null) throw new IllegalStateException("cannot address non-data symbol"); 53 | if (this.isParam) return false; // parameters are not addressable! 54 | return true; 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/SeacCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.emulator.MonTanaMiniComputer; 4 | import mtmc.lang.sea.SeaLanguage; 5 | import mtmc.os.fs.FileSystem; 6 | import mtmc.os.shell.ShellCommand; 7 | import mtmc.tokenizer.MTMCTokenizer; 8 | import mtmc.util.StringEscapeUtils; 9 | 10 | public class SeacCommand extends ShellCommand { 11 | @Override 12 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) throws Exception { 13 | String output = "a.out"; 14 | String filename = null; 15 | FileSystem fs = computer.getFileSystem(); 16 | while (tokens.more()) { 17 | var token = tokens.collapseTokensAsString(); 18 | if (token.equals("-o")) { 19 | if (!tokens.more()) throw new IllegalArgumentException("expected filename after '-o'"); 20 | output = tokens.collapseTokensAsString(); 21 | } else { 22 | filename = token; 23 | } 24 | } 25 | 26 | if (filename == null) { 27 | throw new IllegalArgumentException("expected source file"); 28 | } 29 | 30 | if (!fs.exists(filename)) { 31 | throw new IllegalArgumentException("file " + StringEscapeUtils.escapeString(filename) + " does not exist"); 32 | } 33 | System.out.println(fs.resolve(filename)); 34 | SeaLanguage lang = new SeaLanguage(); 35 | String content = fs.readFile(filename); 36 | var exec = lang.compileExecutable(fs.resolve(filename), content); 37 | 38 | String bin = exec.dump(); 39 | computer.getFileSystem().writeFile(output, bin); 40 | computer.notifyOfFileSystemUpdate(); 41 | } 42 | 43 | @Override 44 | public String getHelp() { 45 | return """ 46 | """; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/LoadCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.emulator.MonTanaMiniComputer; 4 | import mtmc.os.exec.Executable; 5 | import mtmc.os.shell.ShellCommand; 6 | import mtmc.tokenizer.MTMCToken; 7 | import mtmc.tokenizer.MTMCTokenizer; 8 | 9 | import java.nio.file.Path; 10 | import mtmc.os.fs.FileSystem; 11 | 12 | public class LoadCommand extends ShellCommand { 13 | static Path getDiskPath(String pathString, FileSystem fs) { 14 | Path path = Path.of("disk" + fs.resolve(pathString)); 15 | return path.toAbsolutePath(); 16 | } 17 | 18 | @Override 19 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) throws Exception { 20 | FileSystem fs = computer.getFileSystem(); 21 | String program = tokens.collapseTokensAsString(); 22 | if (program == null || program.isBlank()) { 23 | throw new IllegalArgumentException("missing or required argument 'src'"); 24 | } 25 | Path srcPath = getDiskPath(program, fs); 26 | 27 | Executable exec = Executable.load(srcPath); 28 | computer.load(exec.code(), exec.data(), exec.graphics(), exec.debugInfo()); 29 | String source = tokens.getSource(); 30 | 31 | // set up an argument if given 32 | if (tokens.more()) { 33 | MTMCToken firstArgToken = tokens.consume(); 34 | int startChar = firstArgToken.start(); 35 | String arg = source.substring(startChar); 36 | String strippedArg = arg.strip(); 37 | if(!strippedArg.isEmpty()) { 38 | computer.setArg(strippedArg); 39 | } 40 | } 41 | } 42 | 43 | @Override 44 | public String getHelp() { 45 | return """ 46 | load 47 | - exec : path to an executable file"""; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 | {% include 'templates/control.html' %} 21 | {% include 'templates/registers.html' %} 22 | {% include 'templates/memory.html' %} 23 |
24 |
25 | {% include 'templates/display.html' %} 26 | {% include 'templates/console.html' %} 27 |
28 |
29 | {% if computer.currentFilename != null %} 30 | {% include computer.selectEditor() %} 31 | {% else %} 32 | {% include 'templates/filetree.html' %} 33 | {% endif %} 34 |
35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/AssembleCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.asm.Assembler; 4 | import mtmc.emulator.MonTanaMiniComputer; 5 | import mtmc.os.shell.ShellCommand; 6 | import mtmc.tokenizer.MTMCTokenizer; 7 | 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import mtmc.os.fs.FileSystem; 11 | 12 | public class AssembleCommand extends ShellCommand { 13 | static Path getDiskPath(String pathString, FileSystem fs) { 14 | Path path = Path.of("disk" + fs.resolve(pathString)); 15 | return path.toAbsolutePath(); 16 | } 17 | 18 | @Override 19 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) throws Exception { 20 | FileSystem fs = computer.getFileSystem(); 21 | String src = tokens.collapseTokensAsString(); 22 | if (src == null || src.isBlank()) { 23 | throw new IllegalArgumentException("missing or required argument 'src'"); 24 | } 25 | Path srcPath = getDiskPath(src, fs); 26 | 27 | String dst = tokens.collapseTokensAsString(); 28 | if (dst == null || dst.isBlank()) { 29 | throw new IllegalArgumentException("missing required argument 'dst'"); 30 | } 31 | Path dstPath = getDiskPath(dst, fs); 32 | 33 | String contents = Files.readString(srcPath); 34 | Assembler assembler = new Assembler(); 35 | var file_name = fs.resolve(src); // srcPath.toString().substring(DISK_PATH.toString().length()).replaceAll("\\\\", "/"); 36 | var executable = assembler.assembleExecutable(file_name, contents); 37 | executable.dump(dstPath); 38 | computer.notifyOfFileSystemUpdate(); 39 | } 40 | 41 | @Override 42 | public String getHelp() { 43 | return """ 44 | asm 45 | - src : path to a .asm file 46 | - dst : path to a target output binary"""; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/graphics/Graphic.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.graphics; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.File; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.util.List; 8 | import javax.imageio.ImageIO; 9 | import mtmc.asm.ASMElement; 10 | import mtmc.emulator.MTMCDisplay; 11 | import mtmc.tokenizer.MTMCToken; 12 | 13 | /** 14 | * 15 | * @author jbanes 16 | */ 17 | public class Graphic extends ASMElement { 18 | 19 | private String filename; 20 | private byte[] data; 21 | 22 | public Graphic(List labels, int lineNumber) { 23 | super(labels, lineNumber); 24 | } 25 | 26 | public void setImage(String filename) { 27 | try { 28 | var image = ImageIO.read(new File(filename)); 29 | var buffer = new ByteArrayOutputStream(); 30 | 31 | if (image.getWidth() > 1024 || image.getHeight() > 1024) { 32 | addError(filename + " is too large. Maximum image size is 1024x1024"); 33 | } 34 | 35 | image = MTMCDisplay.convertImage(image); 36 | 37 | ImageIO.write(image, "png", buffer); 38 | 39 | this.filename = filename; 40 | this.data = buffer.toByteArray(); 41 | } catch(FileNotFoundException e) { 42 | e.printStackTrace(); 43 | addError(filename + " not found"); 44 | } catch(IOException e) { 45 | e.printStackTrace(); 46 | addError(e.getMessage()); // TODO: Verify these messages are meaningful 47 | } 48 | } 49 | 50 | @Override 51 | public void addError(String err) { 52 | addError(getLabels().getLast(), err); 53 | } 54 | 55 | @Override 56 | public int getSizeInBytes() { 57 | return 2; 58 | } 59 | 60 | public byte[] getImageData() { 61 | return data; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/mtmc/emulator/Register.java: -------------------------------------------------------------------------------- 1 | package mtmc.emulator; 2 | 3 | public enum Register { 4 | 5 | //=== user-facing registers 6 | T0, // temp registers 7 | T1, 8 | T2, 9 | T3, 10 | T4, 11 | T5, 12 | A0, // arg registers 13 | A1, 14 | A2, 15 | A3, 16 | RV, // return value 17 | RA, // return address 18 | FP, // frame pointer 19 | SP, // stack pointer 20 | BP, // break pointer 21 | PC, 22 | 23 | //=== non-user-facing registers 24 | IR, // instruction register 25 | DR, // data register 26 | CB, // code boundary 27 | DB, // data boundary 28 | IO, // I/O register 29 | FLAGS; // flags register 30 | 31 | public static int toInteger(String reg) { 32 | return Register.valueOf(reg.toUpperCase()).ordinal(); 33 | } 34 | 35 | public static String fromInteger(int reg) { 36 | return Register.values()[reg].name().toLowerCase(); 37 | } 38 | 39 | public static boolean isWriteable(int reg) { 40 | return 0 <= reg && reg < 16; 41 | } 42 | 43 | public static boolean isReadable(int reg) { 44 | return 0 <= reg && reg < 16; 45 | } 46 | 47 | private static boolean isTempRegister(int reg) { 48 | return 0 <= reg && reg < 6;} 49 | 50 | public static boolean isWriteable(String register) { 51 | try { 52 | return isWriteable(toInteger(register)); 53 | } catch (Exception e) { 54 | return false; 55 | } 56 | } 57 | public static boolean isReadable(String register) { 58 | try { 59 | return isReadable(toInteger(register)); 60 | } catch (Exception e) { 61 | return false; 62 | } 63 | } 64 | 65 | public static boolean isTempRegister(String register) { 66 | try { 67 | return isTempRegister(toInteger(register)); 68 | } catch (Exception e) { 69 | return false; 70 | } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/ast/DeclarationFunc.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea.ast; 2 | 3 | import mtmc.lang.sea.SeaParser; 4 | import mtmc.lang.sea.SeaType; 5 | import mtmc.lang.sea.Token; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public final class DeclarationFunc extends Declaration { 11 | public final TypeExpr returnType; 12 | public final Token name; 13 | public final ParamList params; 14 | public final StatementBlock body; 15 | 16 | public DeclarationFunc(TypeExpr returnType, Token name, ParamList paramList, StatementBlock body, Token end) { 17 | super(returnType.start, end); 18 | this.returnType = returnType; 19 | this.name = name; 20 | this.params = paramList; 21 | this.body = body; 22 | } 23 | 24 | public static final class Param extends Ast { 25 | public final TypeExpr type; 26 | public final Token name; 27 | 28 | public Param(TypeExpr type, Token name) { 29 | super(type.start, name.end() < type.end.end() ? type.end : name); 30 | this.type = type; 31 | this.name = name; 32 | } 33 | } 34 | 35 | private SeaType.Func type; 36 | public SeaType.Func type() { 37 | if (type == null) { 38 | var paramTypes = new ArrayList(params.size()); 39 | for (Param param : params.params) { 40 | paramTypes.add(param.type.type()); 41 | } 42 | type = new SeaType.Func( 43 | paramTypes, 44 | params.isVararg, 45 | returnType.type() 46 | ); 47 | } 48 | return type; 49 | } 50 | 51 | public record ParamList(List params, boolean isVararg) { 52 | public int size() { 53 | return params.size(); 54 | } 55 | 56 | public SeaType getParamType(int i) { 57 | return params.get(i).type.type(); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/mtmc/lang/sea/SeaLanguage.java: -------------------------------------------------------------------------------- 1 | package mtmc.lang.sea; 2 | 3 | import mtmc.lang.CompilationException; 4 | import mtmc.lang.Language; 5 | import mtmc.lang.ParseException; 6 | import mtmc.lang.sea.ast.Error; 7 | import mtmc.lang.sea.ast.Unit; 8 | import mtmc.os.exec.Executable; 9 | 10 | public class SeaLanguage implements Language { 11 | @Override 12 | public Executable compileExecutable(String filename, String source) throws mtmc.lang.ParseException, CompilationException { 13 | var tokens = Token.tokenize(source); 14 | var parser = new SeaParser(filename, source, tokens); 15 | Unit program = parser.parseUnit(); 16 | var errors = program.collectErrors(); 17 | if (!errors.isEmpty()) { 18 | StringBuilder sb = new StringBuilder(); 19 | for (Error error : errors) { 20 | reportError(source, sb, error.exception()); 21 | } 22 | throw new RuntimeException(sb.toString()); 23 | } 24 | 25 | 26 | var compiler = new SeaCompiler(program); 27 | return compiler.compile(); 28 | } 29 | 30 | private static void reportError(String src, StringBuilder sb, ParseException e) { 31 | sb.append("Error:\n"); 32 | for (var msg : e.messages) { 33 | var lo = Token.getLineAndOffset(src, msg.start().start()); 34 | int lineNo = lo[0]; 35 | int column = lo[1]; 36 | var line = Token.getLineFor(src, msg.start().start()); 37 | String prefix = " %03d:%03d | ".formatted(lineNo, column); 38 | String info = " ".repeat(prefix.length() - 2) + "| "; 39 | sb.append(info).append(msg.message()).append('\n'); 40 | sb.append(prefix).append(line).append('\n'); 41 | sb 42 | .repeat(' ', prefix.length() + column - 1) 43 | .repeat('^', Math.max(1, msg.end().end() - msg.start().start())); 44 | sb.append("\n\n"); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/templates/newfile.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | Exploring - {{ computer.getFileSystem().getCWD() }} 5 |
6 | 7 |
8 |
Create New File
9 |
10 |
11 |
12 | 14 |
15 |
16 |
File Type:
17 |
18 | 23 |
24 |
25 | Name: 26 |
27 |
28 | 29 |
30 |
31 | 32 | 33 |
34 |
{{computer.currentError}}
35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/SetCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.emulator.MonTanaMiniComputer; 4 | import mtmc.emulator.Register; 5 | import mtmc.os.shell.ShellCommand; 6 | import mtmc.tokenizer.MTMCToken; 7 | import mtmc.tokenizer.MTMCTokenizer; 8 | 9 | import static mtmc.tokenizer.MTMCToken.TokenType.*; 10 | 11 | public class SetCommand extends ShellCommand { 12 | 13 | @Override 14 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) throws Exception { 15 | MTMCToken memLocation = tokens.matchAndConsume(INTEGER, HEX, BINARY); 16 | if (memLocation == null) { 17 | MTMCToken register = tokens.matchAndConsume(IDENTIFIER); 18 | if (register == null) usageException(); 19 | MTMCToken value = tokens.matchAndConsume(INTEGER, HEX, BINARY); 20 | if (value == null) usageException(); 21 | int reg = Register.toInteger(register.stringValue()); 22 | if (reg >= 0) { 23 | computer.setRegisterValue(reg, value.intValue()); 24 | } else { 25 | throw new IllegalArgumentException("Bad register: " + register.stringValue()); 26 | } 27 | } else { 28 | MTMCToken value = tokens.matchAndConsume(INTEGER, HEX, BINARY, STRING); 29 | if (value == null) usageException(); 30 | if (value.type() == INTEGER || value.type() == BINARY || value.type() == HEX) { 31 | computer.writeWordToMemory(memLocation.intValue(), value.intValue().shortValue()); 32 | } else { 33 | computer.writeStringToMemory(memLocation.intValue(), value.stringValue().getBytes()); 34 | } 35 | } 36 | } 37 | 38 | @Override 39 | public String getHelp() { 40 | return """ 41 | set - sets a memory location value 42 | loc: a register name or memory location 43 | value: an integer, hex or binary value, or, for memory locations, a quoted string"""; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /disk/src/utils/ls.asm: -------------------------------------------------------------------------------- 1 | .data 2 | count: -1 3 | path: -1 4 | newline: "\n" 5 | slash: "/" 6 | invalid_dir: "INVALID DIRECTORY" 7 | 8 | file: 9 | # Output Directory Entry 10 | file_flags: 0 11 | file_name_size: 256 12 | file_name: .byte 256 13 | 14 | dir: 15 | # Input Directory 16 | dir_name_size: 256 17 | dir_name: .byte 256 18 | 19 | 20 | .text 21 | main: 22 | # Check if a path was passed 23 | eqi a0 0 24 | sw a0 path 25 | jnz get_cwd 26 | 27 | # Copy the passed in name to dir_name 28 | mov t0 a0 29 | li t1 dir_name 30 | lw t2 dir_name_size 31 | 32 | loop_dir_load: 33 | sw t0 path 34 | lbo t3 t0 0 35 | sbo t3 t1 0 36 | 37 | inc t0 38 | inc t1 39 | dec t2 40 | 41 | # NULL terminator found 42 | eqi t3 0 43 | jnz start 44 | 45 | # Check max size 46 | eqi t2 0 47 | jz loop_dir_load 48 | 49 | j start 50 | 51 | get_cwd: 52 | # Get current working directory 53 | li a0 dir_name 54 | lw a1 dir_name_size 55 | 56 | sys cwd 57 | 58 | start: 59 | li a0 dir_name 60 | li a1 0 61 | 62 | sys dirent 63 | 64 | sw rv count 65 | lw t0 count 66 | li t1 -1 67 | eq t0 t1 68 | jnz print_invalid_dir 69 | 70 | # If no files, jump to end 71 | eqi t0 0 72 | jnz done 73 | 74 | # loop over files 75 | li t0 0 76 | 77 | loop_file_list: 78 | li a0 dir_name 79 | li a1 1 80 | mov a2 t0 81 | li a3 file 82 | 83 | sys dirent 84 | 85 | li a0 file_name 86 | sys wstr 87 | 88 | # Print / if directory 89 | li t2 1 90 | lw t3 file_flags 91 | eq t2 t3 92 | jz print_newline 93 | 94 | li a0 slash 95 | sys wstr 96 | 97 | print_newline: 98 | li a0 newline 99 | sys wstr 100 | 101 | inc t0 102 | lw t1 count 103 | eq t0 t1 104 | jz loop_file_list 105 | 106 | j done 107 | 108 | print_invalid_dir: 109 | li a0 invalid_dir 110 | li a1 256 111 | li t3 invalid_dir 112 | sys wstr 113 | 114 | done: 115 | sys exit 116 | -------------------------------------------------------------------------------- /disk/bin/ant: -------------------------------------------------------------------------------- 1 | {"format":"Orc1","code":[-113,0,0,78,-113,16,0,68,0,58,1,42,1,50,31,53,0,-128,-48,22,3,20,1,50,31,53,0,64,-48,32,2,20,1,50,31,53,0,32,-48,42,3,4,1,50,31,53,0,16,-48,52,2,4,60,0,-48,60,-113,0,0,-97,60,16,-48,68,-113,16,0,-117,31,4,0,-96,31,20,0,-112,0,48,1,96,1,113,-113,-128,0,4,-113,-112,0,4,0,52,0,53,-113,96,0,100,0,33,-64,8],"data":[],"graphics":[],"sourceName":"/src/games/ant.asm","debugInfo":{"debugStrings":[],"assemblyFile":"/src/games/ant.asm","assemblySource":".text\nmain:\n li t0 78 # ant col\n li t1 68 # ant row\n\n loop:\n\n # get the latest IO\n sys joystick\n mov t2 rv\n\n mov t3 t2\n andi t3 0b1000_0000 # up\n jz l1\n dec t1 4\n l1:\n\n mov t3 t2\n andi t3 0b0100_0000 # down\n jz l2\n inc t1 4\n l2:\n\n mov t3 t2\n andi t3 0b0010_0000 # left\n jz l3\n dec t0 4\n l3:\n\n mov t3 t2\n andi t3 0b0001_0000 # right\n jz l4\n inc t0 4\n l4:\n\n lti t0 0\n jz l5\n li t0 159\n l5:\n\n lti t1 0\n jz l6\n li t1 139\n l6:\n\n modi t0 160\n modi t1 144\n\n sys fbreset # reset the frame buffer\n\n mov a0 t0 # move x and y into args\n mov a1 t1\n li a2 4 # 4x4 rectangle\n li a3 4\n sys fbrect # draw the ant\n\n sys fbflush # sync the screen\n\n li a0 100\n sys sleep # sleep 100 millis\n\n j loop # do it all again\n","assemblyLineNumbers":[3,3,3,3,4,4,4,4,9,9,10,10,12,12,13,13,13,13,14,14,15,15,18,18,19,19,19,19,20,20,21,21,24,24,25,25,25,25,26,26,27,27,30,30,31,31,31,31,32,32,33,33,36,36,37,37,38,38,38,38,41,41,42,42,43,43,43,43,46,46,46,46,47,47,47,47,49,49,51,51,52,52,53,53,53,53,54,54,54,54,55,55,57,57,59,59,59,59,60,60,62,62],"originalFile":"","originalLineNumbers":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"globals":[],"locals":[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]}} -------------------------------------------------------------------------------- /src/main/java/mtmc/util/BinaryUtils.java: -------------------------------------------------------------------------------- 1 | package mtmc.util; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public class BinaryUtils { 6 | 7 | /** 8 | * @param start - a base 1 index to start at (16 is the most significant, 1 is the least) 9 | * @param totalBits - the total bits to get 10 | * @param value - the value to get the bits from 11 | */ 12 | public static short getBits(int start, int totalBits, short value) { 13 | if (totalBits <= 0) { 14 | return 0; 15 | } 16 | int returnValue = (value & 0xffff) >>> (start - totalBits); 17 | int mask = 0; 18 | int toShift = totalBits; 19 | while(toShift > 0) { 20 | toShift--; 21 | mask = mask << 1; 22 | mask = mask + 1; 23 | } 24 | return (short) (returnValue & mask); 25 | } 26 | 27 | @NotNull 28 | public static String toBinary(byte aByte) { 29 | String binaryString = Integer.toBinaryString(aByte); 30 | String formatted = String.format("%8s", binaryString); 31 | String zeroed = formatted.replaceAll(" ", "0"); 32 | String underScored = zeroed.replaceAll("....", "$0_"); 33 | String noTrailingUnderscore = underScored.substring(0, underScored.length() - 1); 34 | return "0b" + noTrailingUnderscore; 35 | } 36 | 37 | @NotNull 38 | public static String toBinary(short aShort) { 39 | String binaryString = Integer.toBinaryString(aShort); 40 | String formatted = String.format("%16s", binaryString); 41 | String zeroed = formatted.replaceAll(" ", "0"); 42 | String underScored = zeroed.replaceAll("....", "$0_"); 43 | String noTrailingUnderscore = underScored.substring(0, underScored.length() - 1); 44 | return "0b" + noTrailingUnderscore; 45 | } 46 | 47 | @NotNull 48 | public static String toBinary(int anInt) { 49 | String binaryString = Integer.toBinaryString(anInt); 50 | String formatted = String.format("%32s", binaryString); 51 | String zeroed = formatted.replaceAll(" ", "0"); 52 | String underScored = zeroed.replaceAll("....", "$0_"); 53 | String noTrailingUnderscore = underScored.substring(0, underScored.length() - 1); 54 | return "0b" + noTrailingUnderscore; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/mtmc/emulator/MTMCClock.java: -------------------------------------------------------------------------------- 1 | package mtmc.emulator; 2 | 3 | import static mtmc.emulator.MonTanaMiniComputer.ComputerStatus.*; 4 | 5 | /** 6 | * 7 | * @author jbanes 8 | */ 9 | public class MTMCClock 10 | { 11 | private MonTanaMiniComputer computer; 12 | 13 | public MTMCClock(MonTanaMiniComputer computer) { 14 | this.computer = computer; 15 | } 16 | 17 | public void run() { 18 | 19 | long instructions = 0; 20 | long ips = 0; 21 | long expected = 0; 22 | long virtual = 0; 23 | 24 | long startTime = System.currentTimeMillis(); 25 | long deltaStart; 26 | long delta; 27 | 28 | long speed = 0; 29 | long pulse; 30 | long ms = 10; 31 | 32 | while(computer.getStatus() == EXECUTING) { 33 | speed = Math.max(computer.getSpeed(), 0); 34 | pulse = (speed <= 0 ? 1000000 : Math.max(speed / 100, 1)); 35 | ms = (pulse < 10 ? 1000 / speed : 10); 36 | 37 | deltaStart = System.currentTimeMillis(); 38 | delta = ms - (System.currentTimeMillis() - deltaStart); 39 | 40 | /* We've lost more than a second. Recalibrate. */ 41 | if ((expected - virtual) > pulse * 100) { 42 | startTime = deltaStart; 43 | virtual = 0; 44 | } 45 | 46 | /* Throttles to every 10ms, but "catches up" if we're behind */ 47 | if(delta > 0 && (expected - virtual) < pulse && speed != 0) { 48 | try { Thread.sleep(delta); } catch(InterruptedException e) {} 49 | } 50 | 51 | instructions += computer.pulse(pulse); 52 | 53 | virtual += pulse; 54 | ips = (virtual * 1000) / Math.max(1, System.currentTimeMillis() - startTime); 55 | expected = (System.currentTimeMillis() - startTime) * speed / 1000; 56 | } 57 | 58 | System.err.println("Executed " + instructions + " instructions at a rate of " + ips + " ips (speed = " + speed + ")"); 59 | } 60 | 61 | public void step() { 62 | computer.fetchAndExecute(); 63 | computer.fetchCurrentInstruction(); 64 | computer.notifyOfStepExecution(); 65 | } 66 | 67 | public void back() { 68 | computer.rewind(); 69 | computer.fetchCurrentInstruction(); 70 | computer.notifyOfStepExecution(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/instructions/JumpInstruction.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.instructions; 2 | 3 | import mtmc.asm.Assembler; 4 | import mtmc.tokenizer.MTMCToken; 5 | 6 | import java.util.List; 7 | 8 | import static mtmc.util.BinaryUtils.getBits; 9 | 10 | public class JumpInstruction extends Instruction { 11 | 12 | private MTMCToken addressToken; 13 | 14 | public JumpInstruction(InstructionType type, List label, MTMCToken instructionToken) { 15 | super(type, label, instructionToken); 16 | } 17 | 18 | public void setAddressToken(MTMCToken addressToken) { 19 | this.addressToken = addressToken; 20 | } 21 | 22 | @Override 23 | public void validateLabel(Assembler assembler) { 24 | if (addressToken.type() == MTMCToken.TokenType.IDENTIFIER) { 25 | if (!assembler.hasLabel(addressToken.stringValue())) { 26 | addError("Unresolved label: " + addressToken.stringValue()); 27 | } 28 | } 29 | } 30 | 31 | private Integer resolveTargetAddress(Assembler assembler) { 32 | if (addressToken.type() == MTMCToken.TokenType.IDENTIFIER) { 33 | return assembler.resolveLabel(addressToken.stringValue()); 34 | } else { 35 | return addressToken.intValue(); 36 | } 37 | } 38 | 39 | @Override 40 | public void genCode(byte[] output, Assembler assembler) { 41 | int opcode = 0; 42 | switch (getType()) { 43 | case J -> opcode = 0b1100; 44 | case JZ -> opcode = 0b1101; 45 | case JNZ -> opcode = 0b1110; 46 | case JAL -> opcode = 0b1111; 47 | } 48 | int address = resolveTargetAddress(assembler); 49 | output[getLocation()] = (byte) (opcode << 4 | address >>> 8); 50 | output[getLocation()+1] = (byte) address; 51 | } 52 | 53 | public static String disassemble(short instruction) { 54 | if (getBits(16, 2, instruction) == 0b11) { 55 | short jumpType = getBits(14, 2, instruction); 56 | StringBuilder sb = new StringBuilder(); 57 | if (jumpType == 0b00) { 58 | sb.append("j"); 59 | } else if (jumpType == 0b01) { 60 | sb.append("jz"); 61 | } else if (jumpType == 0b10) { 62 | sb.append("jnz"); 63 | } else if (jumpType == 0b11) { 64 | sb.append("jal"); 65 | } 66 | short target = getBits(12, 12, instruction); 67 | sb.append(" ").append(target); 68 | return sb.toString(); 69 | } 70 | return null; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/mtmc/tokenizer/MTMCToken.java: -------------------------------------------------------------------------------- 1 | package mtmc.tokenizer; 2 | 3 | public record MTMCToken( 4 | int start, 5 | int end, 6 | int line, 7 | int lineOffset, 8 | String stringValue, 9 | TokenType type 10 | ) { 11 | public static MTMCToken join(MTMCToken a, MTMCToken b, TokenType type) { 12 | if (a.end != b.start) throw new IllegalArgumentException("tokens must be joint!"); 13 | return new MTMCToken( 14 | a.start, 15 | b.end, 16 | a.line, 17 | a.lineOffset, 18 | a.stringValue + b.stringValue, 19 | type 20 | ); 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return stringValue; 26 | } 27 | 28 | public String stringValue() { 29 | return stringValue; 30 | } 31 | 32 | public char charValue() { 33 | return stringValue.charAt(0); 34 | } 35 | 36 | public Integer intValue() { 37 | if (type == TokenType.INTEGER) { 38 | return Integer.parseInt(stringValue); 39 | } else if (type == TokenType.HEX) { 40 | String stripped = stringValue.substring(2); 41 | short i = Short.parseShort(stripped, 16); 42 | return (int) i; 43 | } else if (type == TokenType.BINARY) { 44 | String stripped = stringValue.substring(2); 45 | short i = Short.parseShort(stripped.replaceAll("_", ""), 2); 46 | return (int) i; 47 | } else { 48 | throw new UnsupportedOperationException("Cannot return int for type " + type()); 49 | } 50 | } 51 | 52 | public String labelValue() { 53 | return stringValue.substring(0, stringValue.length() - 1); 54 | } 55 | 56 | public MTMCToken cloneWithVal(String val) { 57 | return new MTMCToken(start, end, line, lineOffset, val, type); 58 | } 59 | 60 | public enum TokenType { 61 | LEFT_PAREN, 62 | RIGHT_PAREN, 63 | LEFT_BRACE, 64 | RIGHT_BRACE, 65 | LEFT_BRACKET, 66 | RIGHT_BRACKET, 67 | COLON, 68 | COMMA, 69 | DOT, 70 | MINUS, 71 | PLUS, 72 | SLASH, 73 | AT, 74 | STAR, 75 | QUESTION_MARK, 76 | BANG, 77 | BANG_EQUAL, 78 | EQUAL, 79 | EQUAL_EQUAL, 80 | GREATER, 81 | GREATER_EQUAL, 82 | LESS, LESS_EQUAL, 83 | IDENTIFIER, 84 | LABEL, 85 | STRING, 86 | CHAR, 87 | INTEGER, 88 | DECIMAL, 89 | HEX, 90 | BINARY, 91 | ERROR, 92 | SOF, 93 | EOF 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/instructions/InstructionType.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.instructions; 2 | 3 | import static mtmc.asm.instructions.InstructionType.InstructionClass.*; 4 | 5 | public enum InstructionType { 6 | SYS(MISC), 7 | MOV(MISC), 8 | INC(MISC), 9 | DEC(MISC), 10 | SETI(MISC), 11 | NOP(MISC), 12 | MCP(MISC, 4), 13 | DEBUG(MISC), 14 | ADD(ALU), 15 | SUB(ALU), 16 | MUL(ALU), 17 | DIV(ALU), 18 | MOD(ALU), 19 | AND(ALU), 20 | OR(ALU), 21 | XOR(ALU), 22 | SHL(ALU), 23 | SHR(ALU), 24 | MIN(ALU), 25 | MAX(ALU), 26 | NOT(ALU), 27 | LNOT(ALU), 28 | NEG(ALU), 29 | IMM(ALU, 4), 30 | PUSH(STACK), 31 | POP(STACK), 32 | DUP(STACK), 33 | SWAP(STACK), 34 | DROP(STACK), 35 | OVER(STACK), 36 | ROT(STACK), 37 | SOP(STACK), 38 | PUSHI(STACK, 4), 39 | EQ(TEST), 40 | NEQ(TEST), 41 | GT(TEST), 42 | GTE(TEST), 43 | LT(TEST), 44 | LTE(TEST), 45 | EQI(TEST), 46 | NEQI(TEST), 47 | GTI(TEST), 48 | GTEI(TEST), 49 | LTI(TEST), 50 | LTEI(TEST), 51 | LWR(LOAD_STORE_REGISTER), 52 | LBR(LOAD_STORE_REGISTER), 53 | SWR(LOAD_STORE_REGISTER), 54 | SBR(LOAD_STORE_REGISTER), 55 | LW(LOAD_STORE, 4), 56 | LWO(LOAD_STORE, 4), 57 | LI(LOAD_STORE, 4), 58 | LB(LOAD_STORE, 4), 59 | LBO(LOAD_STORE, 4), 60 | SW(LOAD_STORE, 4), 61 | SWO(LOAD_STORE, 4), 62 | SB(LOAD_STORE, 4), 63 | SBO(LOAD_STORE, 4), 64 | JR(JUMP_REGISTER), 65 | J(JUMP), 66 | JZ(JUMP), 67 | JNZ(JUMP), 68 | JAL(JUMP), 69 | ; 70 | 71 | 72 | public int getSizeInBytes() { 73 | return size; 74 | } 75 | 76 | public enum InstructionClass { 77 | MISC, 78 | ALU, 79 | STACK, 80 | TEST, 81 | LOAD_STORE_REGISTER, 82 | LOAD_STORE, 83 | JUMP_REGISTER, 84 | JUMP 85 | } 86 | 87 | private final InstructionClass instructionClass; 88 | private final int size; 89 | 90 | InstructionType(InstructionClass instructionClass) { 91 | this(instructionClass, 2); 92 | } 93 | 94 | InstructionType(InstructionClass instructionClass, int size) { 95 | this.instructionClass = instructionClass; 96 | this.size = size; 97 | } 98 | 99 | public InstructionClass getInstructionClass() { 100 | return instructionClass; 101 | } 102 | 103 | public static InstructionType fromString(String string) { 104 | try { 105 | return InstructionType.valueOf(string.toUpperCase()); 106 | } catch (IllegalArgumentException e) { 107 | return null; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/test/java/mtmc/emulator/EndToEndTests.java: -------------------------------------------------------------------------------- 1 | package mtmc.emulator; 2 | 3 | import mtmc.asm.Assembler; 4 | import mtmc.asm.AssemblyResult; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static mtmc.emulator.Register.*; 8 | import static org.junit.jupiter.api.Assertions.*; 9 | 10 | public class EndToEndTests { 11 | 12 | @Test 13 | public void addOneToOne(){ 14 | Assembler assembler = new Assembler(); 15 | AssemblyResult result = assembler.assemble(""" 16 | li t0 1 17 | add t0 t0 18 | """); 19 | MonTanaMiniComputer computer = new MonTanaMiniComputer(); 20 | computer.load(result.code(), result.data(), result.debugInfo()); 21 | computer.run(); 22 | assertEquals(2, computer.getRegisterValue(T0)); 23 | } 24 | 25 | @Test 26 | public void stackAddition(){ 27 | Assembler assembler = new Assembler(); 28 | AssemblyResult result = assembler.assemble(""" 29 | pushi 10 30 | pushi 99 31 | sop add 32 | pushi 99 33 | sop add 34 | pop t0 35 | """); 36 | MonTanaMiniComputer computer = new MonTanaMiniComputer(); 37 | computer.load(result.code(), result.data(), result.debugInfo()); 38 | computer.run(); 39 | assertEquals(208, computer.getRegisterValue(T0)); 40 | } 41 | 42 | @Test 43 | public void helloWorld(){ 44 | Assembler assembler = new Assembler(); 45 | AssemblyResult result = assembler.assemble(""" 46 | .data 47 | hello_world: "hello world" 48 | .text 49 | li a0 hello_world 50 | sys wstr 51 | """); 52 | MonTanaMiniComputer computer = new MonTanaMiniComputer(); 53 | computer.load(result.code(), result.data(), result.debugInfo()); 54 | computer.run(); 55 | assertEquals("hello world", computer.getConsole().getOutput()); 56 | } 57 | 58 | @Test 59 | public void negativeImmediates() { 60 | Assembler assembler = new Assembler(); 61 | AssemblyResult result = assembler.assemble(""" 62 | .data 63 | value: -1 64 | .text 65 | lw a0 value 66 | sys wint 67 | sys exit 68 | """); 69 | if (!result.errors().isEmpty()) { 70 | fail(result.printErrors()); 71 | } 72 | 73 | MonTanaMiniComputer computer = new MonTanaMiniComputer(); 74 | computer.load(result.code(), result.data(), result.debugInfo()); 75 | computer.run(); 76 | var output = computer.getConsole().getOutput(); 77 | System.out.println(output); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/instructions/Instruction.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.instructions; 2 | 3 | import mtmc.asm.ASMElement; 4 | import mtmc.asm.Assembler; 5 | import mtmc.emulator.MonTanaMiniComputer; 6 | import mtmc.tokenizer.MTMCToken; 7 | 8 | import java.util.List; 9 | 10 | public abstract class Instruction extends ASMElement { 11 | 12 | private final InstructionType type; 13 | private final MTMCToken instructionToken; 14 | 15 | public static boolean isInstruction(String cmd) { 16 | return InstructionType.fromString(cmd) != null; 17 | } 18 | 19 | public MTMCToken getInstructionToken() { 20 | return instructionToken; 21 | } 22 | 23 | public void validateLabel(Assembler assembler) { 24 | // default does nothing 25 | } 26 | 27 | public Instruction(InstructionType type, List labels, MTMCToken instructionToken) { 28 | super(labels, instructionToken.line()); 29 | this.type = type; 30 | this.instructionToken = instructionToken; 31 | } 32 | 33 | public void addError(String error) { 34 | addError(instructionToken, error); 35 | } 36 | 37 | public InstructionType getType() { 38 | return type; 39 | } 40 | 41 | public abstract void genCode(byte[] output, Assembler assembler); 42 | 43 | @Override 44 | public int getSizeInBytes() { 45 | return type == null ? 0 : type.getSizeInBytes(); 46 | } 47 | 48 | public static String disassemble(short instruction, short previousInstruction) { 49 | if (MonTanaMiniComputer.isDoubleWordInstruction(previousInstruction)) { 50 | return String.valueOf(instruction); 51 | } 52 | String misc = MiscInstruction.disassemble(instruction); 53 | if (misc != null) { 54 | return misc; 55 | } 56 | String aluOp = ALUInstruction.disassemble(instruction); 57 | if (aluOp != null) { 58 | return aluOp; 59 | } 60 | String stack = StackInstruction.disassemble(instruction); 61 | if (stack != null) { 62 | return stack; 63 | } 64 | String test = TestInstruction.disassemble(instruction); 65 | if (test != null) { 66 | return test; 67 | } 68 | String lsr = LoadStoreRegisterInstruction.disassemble(instruction); 69 | if (lsr != null) { 70 | return lsr; 71 | } 72 | String ls = LoadStoreInstruction.disassemble(instruction); 73 | if (ls != null) { 74 | return ls; 75 | } 76 | String jumpReg = JumpRegisterInstruction.disassemble(instruction); 77 | if (jumpReg != null) { 78 | return jumpReg; 79 | } 80 | String jump = JumpInstruction.disassemble(instruction); 81 | if (jump != null) { 82 | return jump; 83 | } 84 | return ""; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/instructions/LoadStoreRegisterInstruction.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.instructions; 2 | 3 | import mtmc.asm.Assembler; 4 | import mtmc.emulator.Register; 5 | import mtmc.tokenizer.MTMCToken; 6 | 7 | import java.util.List; 8 | 9 | import static mtmc.util.BinaryUtils.getBits; 10 | 11 | public class LoadStoreRegisterInstruction extends Instruction { 12 | 13 | private MTMCToken targetToken; 14 | private MTMCToken pointerToken; 15 | private MTMCToken offsetToken; 16 | 17 | public LoadStoreRegisterInstruction(InstructionType type, List label, MTMCToken instructionToken) { 18 | super(type, label, instructionToken); 19 | } 20 | 21 | public void setTargetToken(MTMCToken targetToken) { 22 | this.targetToken = targetToken; 23 | } 24 | 25 | public void setPointerToken(MTMCToken pointerToken) { 26 | this.pointerToken = pointerToken; 27 | } 28 | 29 | public void setOffsetToken(MTMCToken offsetToken) { 30 | this.offsetToken = offsetToken; 31 | } 32 | 33 | @Override 34 | public void genCode(byte[] output, Assembler assembler) { 35 | int opcode = 0; 36 | switch (getType()) { 37 | case LWR -> opcode = 0b0100; 38 | case LBR -> opcode = 0b0101; 39 | case SWR -> opcode = 0b0110; 40 | case SBR -> opcode = 0b0111; 41 | } 42 | int target = Register.toInteger(targetToken.stringValue()); 43 | int pointer = Register.toInteger(pointerToken.stringValue()); 44 | int offset = Register.PC.ordinal(); 45 | if (offsetToken != null) { 46 | offset = Register.toInteger(offsetToken.stringValue()); 47 | } 48 | output[getLocation()] = (byte) (opcode << 4 | target); 49 | output[getLocation() + 1] = (byte) (pointer << 4 | offset); 50 | } 51 | 52 | public static String disassemble(short instruction) { 53 | if (getBits(16, 2, instruction) == 0b01) { 54 | StringBuilder builder = new StringBuilder(); 55 | short type = getBits(14, 2, instruction); 56 | if (type == 0b00) { 57 | builder.append("lwr "); 58 | } else if (type == 0b01) { 59 | builder.append("lbr "); 60 | } else if (type == 0b10) { 61 | builder.append("swr "); 62 | } else if (type == 0b11) { 63 | builder.append("sbr "); 64 | } 65 | short srcDestReg = getBits(12, 4, instruction); 66 | short addrReg = getBits(8, 4, instruction); 67 | short offsetReg = getBits(4, 4, instruction); 68 | builder.append(Register.fromInteger(srcDestReg)).append(" "); 69 | builder.append(Register.fromInteger(addrReg)).append(" "); 70 | builder.append(Register.fromInteger(offsetReg)); 71 | return builder.toString(); 72 | } 73 | return null; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/resources/templates/control.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | msu  MTMC-16 5 |
6 |
7 | MonTana Mini-Computer 8 |
9 |
10 | 28 | {% if not computer.executing %} 29 | 32 | 36 | 39 | {% else %} 40 | 43 | 46 | 49 | {% endif %} 50 | 53 |
54 |
55 |
56 | 57 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | edu.montana.cs 8 | mtmc 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-assembly-plugin 15 | 3.6.0 16 | 17 | 18 | package 19 | 20 | single 21 | 22 | 23 | 24 | 25 | mtmc.web.WebServer 26 | 27 | 28 | 29 | jar-with-dependencies 30 | 31 | mtmc 32 | false 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 21 41 | 21 42 | UTF-8 43 | 44 | 45 | 46 | org.junit.jupiter 47 | junit-jupiter 48 | 5.10.1 49 | test 50 | 51 | 52 | io.javalin 53 | javalin 54 | 6.5.0 55 | 56 | 57 | io.pebbletemplates 58 | pebble 59 | 3.2.3 60 | 61 | 62 | org.slf4j 63 | slf4j-simple 64 | 2.0.16 65 | 66 | 67 | com.google.code.gson 68 | gson 69 | 2.12.1 70 | 71 | 72 | org.webjars.npm 73 | monaco-editor 74 | 0.52.2 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/instructions/MetaInstruction.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.instructions; 2 | 3 | import mtmc.asm.Assembler; 4 | import mtmc.tokenizer.MTMCToken; 5 | 6 | import java.util.List; 7 | 8 | public class MetaInstruction extends Instruction { 9 | private MTMCToken originalFilePath; 10 | private MTMCToken originaLineNumber; 11 | private MTMCToken globalName; 12 | private MTMCToken globalLocation; 13 | private MTMCToken globalType; 14 | private MTMCToken localName; 15 | private MTMCToken localOffset; 16 | private MTMCToken localType; 17 | 18 | public MetaInstruction(MTMCToken instruction) { 19 | super(null, List.of(), instruction); 20 | } 21 | 22 | @Override 23 | public void genCode(byte[] output, Assembler assembler) { 24 | // do nothing 25 | } 26 | 27 | public boolean isFileDirective() { 28 | return "file".equals(this.getInstructionToken().stringValue()); 29 | } 30 | 31 | public boolean isLineDirective() { 32 | return "line".equals(this.getInstructionToken().stringValue()); 33 | } 34 | 35 | public boolean isGlobalDirective() { 36 | return "global".equals(this.getInstructionToken().stringValue()); 37 | } 38 | 39 | public boolean isLocalDirective() { 40 | return "local".equals(this.getInstructionToken().stringValue()); 41 | } 42 | 43 | public boolean isEndLocalDirective() { 44 | return "endlocal".equals(this.getInstructionToken().stringValue()); 45 | } 46 | 47 | public void setOriginalFilePath(MTMCToken path) { 48 | this.originalFilePath = path; 49 | } 50 | 51 | public void setOriginalLineNumber(MTMCToken lineNumber) { 52 | this.originaLineNumber = lineNumber; 53 | } 54 | 55 | public int getOriginalLineNumber() { 56 | return this.originaLineNumber.intValue(); 57 | } 58 | 59 | public void setGlobalInfo(MTMCToken name, MTMCToken location, MTMCToken type) { 60 | this.globalName = name; 61 | this.globalLocation = location; 62 | this.globalType = type; 63 | } 64 | 65 | public void setLocalInfo(MTMCToken name, MTMCToken offset, MTMCToken type) { 66 | this.localName = name; 67 | this.localOffset = offset; 68 | this.localType = type; 69 | } 70 | 71 | public void setEndLocalInfo(MTMCToken name) { 72 | this.localName = name; 73 | } 74 | 75 | public String getOriginalFilePath() { 76 | return this.originalFilePath.stringValue(); 77 | } 78 | 79 | public String getGlobalName() { 80 | return this.globalName.stringValue(); 81 | } 82 | 83 | public int getGlobalLocation() { 84 | return this.globalLocation.intValue(); 85 | } 86 | 87 | public String getGlobalType() { 88 | return this.globalType.stringValue(); 89 | } 90 | 91 | public String getLocalName() { 92 | return this.localName.stringValue(); 93 | } 94 | 95 | public int getLocalOffset() { 96 | return this.localOffset.intValue(); 97 | } 98 | 99 | public String getLocalType() { 100 | return this.localType.stringValue(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /disk/bin/square: -------------------------------------------------------------------------------- 1 | {"format":"Orc1","code":[1,-51,-16,40,1,106,0,0,0,7,1,-41,-112,11,0,2,-112,11,0,8,-112,11,32,-51,1,-51,32,109,32,109,39,45,33,-83,1,-36,33,-51,-112,11,32,-51,1,-51,3,-44,1,70,-113,80,0,0,48,69,-48,62,-113,0,0,1,-64,66,-113,0,0,0,56,0,-32,100,32,109,32,-67,-113,96,0,-96,1,125,-16,8,33,-67,33,109,1,10,-113,-96,0,1,30,-96,1,-36,33,-51,-112,11,32,109,32,-67,-16,18,33,-67,33,109,1,10,-123,12,-1,-2,32,109,32,-67,-127,108,-1,-2,-16,22,33,-67,33,109,1,10,-123,12,-1,-4,32,109,32,-67,-127,108,-1,-4,-16,14,33,-67,33,109,-113,-96,0,0,1,-36,33,-51,-112,11],"data":[117,115,97,103,101,58,32,115,113,117,97,114,101,32,60,110,117,109,98,101,114,62,10,0],"graphics":[],"sourceName":"program","debugInfo":{"debugStrings":[],"assemblyFile":"program","assemblySource":"@file \"/src/square.sea\"\n.data\n strADD26815: \"usage: square \u003cnumber\u003e\\n\"\n\n.text\nmov fp sp\njal main\nmov a0 rv\nsys exit\nprintf:\n sys printf\n mov sp a1\n ret\nputn:\n sys wint\n ret\natoi:\n sys atoi\n ret\nsquare:\n@line 7\n push fp\n mov fp sp\n@line 8\n push a0\n push a0\n smul\n pop rv\n mov sp fp\n pop fp\n ret\nmain:\n@line 12\n push fp\n mov fp sp\n dec sp 4\n@line 13\n mov t4 a0\n li t5 0\n eq t4 t5\n jz cmpFalse2\n li t0 1\n j cmpEnd2\ncmpFalse2:\n li t0 0\ncmpEnd2:\n eqi t0 0\n jnz endIf1\n@line 14\n# stmt expr\n push a0\n push ra\n la a0 strADD26815\n mov a1 sp\n jal printf\n pop ra\n pop a0\n mov t0 rv\n@line 15\n li rv 1\n neg rv\n mov sp fp\n pop fp\n ret\nendIf1:\n@line 17\n# var val\n push a0\n push ra\n jal atoi\n pop ra\n pop a0\n mov t0 rv\n swo t0 fp -2\n@line 18\n# var result\n push a0\n push ra\n lwo a0 fp -2\n jal square\n pop ra\n pop a0\n mov t0 rv\n swo t0 fp -4\n@line 19\n# stmt expr\n push a0\n push ra\n lwo a0 fp -4\n jal putn\n pop ra\n pop a0\n@line 20\n li rv 0\n mov sp fp\n pop fp\n ret\n","assemblyLineNumbers":[6,6,7,7,8,8,9,9,11,11,12,12,13,13,15,15,16,16,18,18,19,19,22,22,23,23,25,25,26,26,27,27,28,28,29,29,30,30,31,31,34,34,35,35,36,36,38,38,39,39,39,39,40,40,41,41,42,42,42,42,43,43,45,45,45,45,47,47,48,48,51,51,52,52,53,53,53,53,54,54,55,55,56,56,57,57,58,58,60,60,60,60,61,61,62,62,63,63,64,64,68,68,69,69,70,70,71,71,72,72,73,73,74,74,74,74,77,77,78,78,79,79,79,79,80,80,81,81,82,82,83,83,84,84,84,84,87,87,88,88,89,89,89,89,90,90,91,91,92,92,94,94,94,94,95,95,96,96,97,97],"originalFile":"/src/square.sea","originalLineNumbers":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20],"globals":[],"locals":[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]}} -------------------------------------------------------------------------------- /src/main/resources/templates/filetree.html: -------------------------------------------------------------------------------- 1 | {% macro render_listing(listing, open) %} 2 |
3 | 4 | 8 | {% if listing.path == "/" %} 9 | / 10 | {% else %} 11 | {{ listing.name }} 12 | {% endif %} 13 | 14 | 15 | 16 |
    17 | {% for file in listing.list() %} 18 | {% if file.directory %} 19 |
  • 20 | {{ render_listing(file, false) }} 21 |
  • 22 | {% endif %} 23 | {% if not file.directory %} 24 |
  • 25 | 26 | {{ file.name }} 32 |
  • 33 | {% endif %} 34 | {% endfor %} 35 |
36 |
37 | {% endmacro %} 38 | 39 |
40 |
41 | 42 | Exploring - {{ computer.getFileSystem().getCWD() }} 43 |
44 | 45 |
46 |
47 | 52 | 57 |
58 |
59 |
60 |
62 | 65 | 66 |
67 |
68 |
69 | {% if computer.getFileSystem().getCWD() != "/" %} 70 |
71 | 72 | 76 | .. 77 | 78 | 79 |
80 | {% endif %} 81 | {{ render_listing(computer.getFileSystem().listCWD(), true) }} 82 |
83 |
-------------------------------------------------------------------------------- /src/main/java/mtmc/os/shell/builtins/DisplayCommand.java: -------------------------------------------------------------------------------- 1 | package mtmc.os.shell.builtins; 2 | 3 | import mtmc.emulator.MTMCDisplay; 4 | import mtmc.emulator.MonTanaMiniComputer; 5 | import mtmc.os.shell.ShellCommand; 6 | import mtmc.tokenizer.MTMCTokenizer; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import javax.imageio.ImageIO; 10 | import java.awt.image.BufferedImage; 11 | import java.io.File; 12 | import java.util.Random; 13 | 14 | import static mtmc.tokenizer.MTMCToken.TokenType.IDENTIFIER; 15 | import static mtmc.tokenizer.MTMCToken.TokenType.INTEGER; 16 | 17 | public class DisplayCommand extends ShellCommand { 18 | 19 | Random random = new Random(); 20 | 21 | @Override 22 | public void exec(MTMCTokenizer tokens, MonTanaMiniComputer computer) throws Exception { 23 | if (tokens.match(IDENTIFIER)) { 24 | String option = tokens.consumeAsString(); 25 | switch (option) { 26 | case "fuzz" -> { 27 | for (int row = 0; row < MTMCDisplay.ROWS; row++) { 28 | for (int col = 0; col < MTMCDisplay.COLS; col++) { 29 | computer.getDisplay().setPixel(col, row, (short) random.nextInt(0, 4)); 30 | } 31 | } 32 | } 33 | case "reset" -> { 34 | computer.getDisplay().reset(); 35 | } 36 | case "invert" -> { 37 | for (int row = 0; row < MTMCDisplay.ROWS; row++) { 38 | for (int col = 0; col < MTMCDisplay.COLS; col++) { 39 | short color = computer.getDisplay().getPixel(col, row); 40 | computer.getDisplay().setPixel(col, row, (short) 3 - color); 41 | } 42 | } 43 | } 44 | case "image" -> { 45 | if (tokens.more()) { 46 | String imagePath = tokens.collapseTokensAsString(); 47 | File file = computer.getOS().loadFile(imagePath); 48 | BufferedImage img = ImageIO.read(file); 49 | computer.getDisplay().loadScaledImage(img); 50 | } else { 51 | usageException(); 52 | } 53 | } 54 | default -> usageException(); 55 | } 56 | } else if (tokens.match(INTEGER)) { 57 | Integer row = tokens.consumeAsInteger(); 58 | Integer col = tokens.require(INTEGER, this::usageException).intValue(); 59 | Integer color = tokens.require(INTEGER, this::usageException).intValue(); 60 | computer.getDisplay().setPixel(row.shortValue(), col.shortValue(), color.shortValue()); 61 | } else { 62 | usageException(); 63 | } 64 | computer.getDisplay().sync(); 65 | } 66 | 67 | @NotNull 68 | private static File loadFile(String imagePath) { 69 | File file = new File("disk/" + imagePath); 70 | return file; 71 | } 72 | 73 | @Override 74 | public String getHelp() { 75 | return """ 76 | disp [options] - updates the display 77 | fuzz - displays random colors 78 | reset - resets the display 79 | invert - inverts the display 80 | image - loads the given image into the display 81 | - sets the given pixel to the given color [0-3]"""; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/instructions/ALUInstruction.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.instructions; 2 | 3 | import mtmc.asm.Assembler; 4 | import mtmc.emulator.Register; 5 | import mtmc.tokenizer.MTMCToken; 6 | 7 | import java.util.List; 8 | 9 | import static mtmc.util.BinaryUtils.getBits; 10 | 11 | public class ALUInstruction extends Instruction { 12 | 13 | private MTMCToken toToken; 14 | private MTMCToken fromToken; 15 | private MTMCToken immediateOp; 16 | private MTMCToken value; 17 | 18 | public ALUInstruction(InstructionType type, List label, MTMCToken instructionToken) { 19 | super(type, label, instructionToken); 20 | } 21 | 22 | public void setTo(MTMCToken to) { 23 | this.toToken = to; 24 | } 25 | 26 | public void setFrom(MTMCToken from) { 27 | this.fromToken = from; 28 | } 29 | 30 | public void setImmediateValue(MTMCToken value) { 31 | this.value = value; 32 | } 33 | 34 | public void setImmediateOp(MTMCToken immediateOp) { 35 | this.immediateOp = immediateOp; 36 | } 37 | 38 | public boolean isImmediateOp() { 39 | return this.getType() == InstructionType.IMM; 40 | } 41 | 42 | public boolean isBinaryOp() { 43 | return !ALUOp.valueOf(getInstructionToken().stringValue().toUpperCase()).isUnary(); 44 | } 45 | 46 | @Override 47 | public void genCode(byte[] output, Assembler assembler) { 48 | int opCode = ALUOp.toInteger(getInstructionToken().stringValue()); 49 | int to = Register.toInteger(toToken.stringValue()); 50 | int from = 0; 51 | if (fromToken != null) { 52 | from = Register.toInteger(fromToken.stringValue()); 53 | } 54 | output[getLocation()] = (byte) (0b0001_0000 | opCode); 55 | if (isBinaryOp()) { 56 | output[getLocation() + 1] = (byte) (to << 4 | from); 57 | } else if (isImmediateOp()) { 58 | int immediateValue = value.intValue(); 59 | int immediateOpValue = ALUOp.toInteger(immediateOp.stringValue()); 60 | output[getLocation() + 1] = (byte) (to << 4 | immediateOpValue); 61 | output[getLocation() + 2] = (byte) (immediateValue >>> 8); 62 | output[getLocation() + 3] = (byte) immediateValue; 63 | } else { // unary op 64 | output[getLocation() + 1] = (byte) (to << 4); 65 | } 66 | } 67 | 68 | public static String disassemble(short instruction) { 69 | if (getBits(16, 4, instruction) == 1) { 70 | StringBuilder builder = new StringBuilder(); 71 | short opCode = getBits(12, 4, instruction); 72 | String op = ALUOp.fromInt(opCode); 73 | ALUOp aluOp = ALUOp.valueOf(op.toUpperCase()); 74 | if (aluOp == ALUOp.IMM) { 75 | builder.append(op).append(" "); 76 | builder.append(Register.fromInteger(getBits(8, 4, instruction))).append(" "); 77 | builder.append(ALUOp.fromInt(getBits(4, 4, instruction))).append(" "); 78 | } else if (aluOp.isUnary()) { 79 | builder.append(op).append(" "); 80 | builder.append(Register.fromInteger(getBits(8, 4, instruction))).append(" "); 81 | } else { 82 | builder.append(op).append(" "); 83 | builder.append(Register.fromInteger(getBits(8, 4, instruction))).append(" "); 84 | builder.append(Register.fromInteger(getBits(4, 4, instruction))).append(" "); 85 | } 86 | return builder.toString(); 87 | } 88 | return null; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/resources/public/js/fixi-0.9.0.js: -------------------------------------------------------------------------------- 1 | (()=>{ 2 | if(document.__fixi_mo) return; 3 | document.__fixi_mo = new MutationObserver((recs)=>recs.forEach((r)=>r.type === "childList" && r.addedNodes.forEach((n)=>process(n)))) 4 | let send = (elt, type, detail, bub)=>elt.dispatchEvent(new CustomEvent("fx:" + type, {detail, cancelable:true, bubbles:bub !== false, composed:true})) 5 | let attr = (elt, name, defaultVal)=>elt.getAttribute(name) || defaultVal 6 | let ignore = (elt)=>elt.closest("[fx-ignore]") != null 7 | let init = (elt)=>{ 8 | let options = {} 9 | if (elt.__fixi || ignore(elt) || !send(elt, "init", {options})) return 10 | elt.__fixi = async(evt)=>{ 11 | let reqs = elt.__fixi.requests ||= new Set() 12 | let form = elt.form || elt.closest("form") 13 | let body = new FormData(form ?? undefined, evt.submitter) 14 | if (!form && elt.name) body.append(elt.name, elt.value) 15 | let ac = new AbortController() 16 | let cfg = { 17 | trigger:evt, 18 | action:attr(elt, "fx-action"), 19 | method:attr(elt, "fx-method", "GET").toUpperCase(), 20 | target:document.querySelector(attr(elt, "fx-target")) ?? elt, 21 | swap:attr(elt, "fx-swap", "outerHTML"), 22 | body, 23 | drop:reqs.size, 24 | headers:{"FX-Request":"true"}, 25 | abort:ac.abort.bind(ac), 26 | signal:ac.signal, 27 | preventTrigger:true, 28 | transition:document.startViewTransition?.bind(document), 29 | fetch:fetch.bind(window) 30 | } 31 | let go = send(elt, "config", {cfg, requests:reqs}) 32 | if (cfg.preventTrigger) evt.preventDefault() 33 | if (!go || cfg.drop) return 34 | if (/GET|DELETE/.test(cfg.method)){ 35 | let params = new URLSearchParams(cfg.body) 36 | if (params.size) 37 | cfg.action += (/\?/.test(cfg.action) ? "&" : "?") + params 38 | cfg.body = null 39 | } 40 | reqs.add(cfg) 41 | try { 42 | if (cfg.confirm){ 43 | let result = await cfg.confirm() 44 | if (!result) return 45 | } 46 | if (!send(elt, "before", {cfg, requests:reqs})) return 47 | cfg.response = await cfg.fetch(cfg.action, cfg) 48 | cfg.text = await cfg.response.text() 49 | if (!send(elt, "after", {cfg})) return 50 | } catch(error) { 51 | send(elt, "error", {cfg, error}) 52 | return 53 | } finally { 54 | reqs.delete(cfg) 55 | send(elt, "finally", {cfg}) 56 | } 57 | let doSwap = ()=>{ 58 | if (cfg.swap instanceof Function) 59 | return cfg.swap(cfg) 60 | else if (/(before|after)(begin|end)/.test(cfg.swap)) 61 | cfg.target.insertAdjacentHTML(cfg.swap, cfg.text) 62 | else if(cfg.swap in cfg.target) 63 | cfg.target[cfg.swap] = cfg.text 64 | else if(cfg.swap !== 'none') throw cfg.swap 65 | } 66 | if (cfg.transition) 67 | await cfg.transition(doSwap).finished 68 | else 69 | await doSwap() 70 | send(elt, "swapped", {cfg}) 71 | if (!document.contains(elt)) send(document, "swapped", {cfg}) 72 | } 73 | elt.__fixi.evt = attr(elt, "fx-trigger", elt.matches("form") ? "submit" : elt.matches("input:not([type=button]),select,textarea") ? "change" : "click") 74 | elt.addEventListener(elt.__fixi.evt, elt.__fixi, options) 75 | send(elt, "inited", {}, false) 76 | } 77 | let process = (n)=>{ 78 | if (n.matches){ 79 | if (ignore(n)) return 80 | if (n.matches("[fx-action]")) init(n) 81 | } 82 | if(n.querySelectorAll) n.querySelectorAll("[fx-action]").forEach(init) 83 | } 84 | document.addEventListener("fx:process", (evt)=>process(evt.target)) 85 | document.addEventListener("DOMContentLoaded", ()=>{ 86 | document.__fixi_mo.observe(document.documentElement, {childList:true, subtree:true}) 87 | process(document.body) 88 | }) 89 | })() 90 | -------------------------------------------------------------------------------- /src/main/java/mtmc/tokenizer/MTMCTokenizer.java: -------------------------------------------------------------------------------- 1 | package mtmc.tokenizer; 2 | 3 | import mtmc.tokenizer.MTMCToken.TokenType; 4 | 5 | import java.util.LinkedList; 6 | import java.util.stream.Stream; 7 | 8 | import static mtmc.tokenizer.MTMCToken.TokenType.*; 9 | 10 | public class MTMCTokenizer { 11 | 12 | String src; 13 | LinkedList tokens; 14 | int currentToken = 0; 15 | 16 | public MTMCTokenizer(String source, String lineCommentStart) { 17 | this.src = source; 18 | tokens = new MTMCScanner(source, lineCommentStart).tokenize(); 19 | } 20 | 21 | public MTMCToken currentToken() { 22 | return tokens.get(currentToken); 23 | } 24 | 25 | public MTMCToken consume() { 26 | return tokens.get(currentToken++); 27 | } 28 | 29 | public String consumeAsString() { 30 | return tokens.get(currentToken++).stringValue(); 31 | } 32 | 33 | public MTMCToken matchAndConsume(TokenType... type) { 34 | if (match(type)) { 35 | return consume(); 36 | } else { 37 | return null; 38 | } 39 | } 40 | 41 | public boolean matchAndConsume(String identifier) { 42 | if (currentToken().type().equals(IDENTIFIER) && 43 | currentToken().stringValue().equals(identifier)) { 44 | return true; 45 | } else { 46 | return false; 47 | } 48 | } 49 | 50 | public boolean match(TokenType... type) { 51 | for (TokenType tokenType : type) { 52 | if (currentToken().type().equals(tokenType)) { 53 | return true; 54 | } 55 | } 56 | return false; 57 | } 58 | 59 | public void reset() { 60 | currentToken = 0; 61 | } 62 | 63 | public boolean more() { 64 | return !currentToken().type().equals(EOF); 65 | } 66 | 67 | public MTMCToken previousToken() { 68 | return tokens.get(Math.max(0, currentToken - 1)); 69 | } 70 | 71 | public Stream stream() { 72 | return tokens.stream(); 73 | } 74 | 75 | @Override 76 | public String toString() { 77 | StringBuilder sb = new StringBuilder(); 78 | for (int i = 0; i < tokens.size(); i++) { 79 | MTMCToken token = tokens.get(i); 80 | if (i == currentToken) { 81 | sb.append("-->["); 82 | } 83 | sb.append(token.stringValue()); 84 | if (i == currentToken) { 85 | sb.append("]<--"); 86 | } 87 | sb.append(" "); 88 | } 89 | return sb.toString(); 90 | } 91 | 92 | // collapse all adjacent tokens into a string 93 | public String collapseTokensAsString() { 94 | if (more()) { 95 | StringBuilder sb = new StringBuilder(); 96 | MTMCToken last, next; 97 | do { 98 | last = consume(); 99 | sb.append(last.stringValue()); 100 | next = currentToken(); 101 | } while (more() && last.end() == next.start()); 102 | return sb.toString(); 103 | } else { 104 | return ""; 105 | } 106 | } 107 | 108 | public Integer consumeAsInteger() { 109 | return consume().intValue(); 110 | } 111 | 112 | public MTMCToken require(TokenType tokenType, Runnable notFound) { 113 | if (match(tokenType)) { 114 | return consume(); 115 | } else { 116 | notFound.run(); 117 | return null; 118 | } 119 | } 120 | 121 | public String getSource() { 122 | return src; 123 | } 124 | } -------------------------------------------------------------------------------- /docs/MTMC_FAQ.md: -------------------------------------------------------------------------------- 1 | 2 | ## Frequently Asked Questions 3 | 4 | ### What does MTMC stand for? 5 | 6 | *Montana State Minicomputer* - named after Montana State University where it was 7 | conceived and developed. *MT* is the two-letter abbreviation for "Montana" while *MC* 8 | is short for "Mini Computer*. 9 | 10 | "Mini Computers" were systems like the PDP-11 that fit within a single rack rather than 11 | a full room like a Mainframe Computer. These ultimately gave way to "Micro Computers" 12 | which were home computers like the Commodore 64 and the IBM PC. 13 | 14 | ### What does the 16 in MTMC-16 stand for? 15 | 16 | The 16 is a reference to the MTMC being a 16-bit computer. This means that 17 | integers are 2 bytes or 16 bits wide. 18 | 19 | This is known as the "word size" of the computer and is based upon the size 20 | of the *Arithmetic Logic Unit* (ALU) in the CPU. The ALU is the part of the 21 | CPU that does math like addition, subtraction, and boolean operations. 22 | 23 | ### What is assembly language? 24 | 25 | Computer CPUs "decode" binary instructions to activate different parts of the CPU 26 | like the *Arithmetic Logic Unit* (ALU) and memory *Control Unit* (CU) for instruction 27 | execution. These components work together to complete the instruction. 28 | 29 | Assembly language prevents programmers from having to manually encode instructions 30 | in binary by providing a text-based, human friendly (properly known as "symbolic") 31 | representation of the instructions. 32 | 33 | While it was common to hand write binary instructions in the early days of 34 | computing, the advent of assemblers made that practice very rare. 35 | 36 | 37 | ### Is MTMC-16 a CISC or RISC processor? 38 | 39 | The MTMC-16 is based on a *Reduced Instruction Set Computing* (RISC) design where 40 | data is loaded into registers before being operated on. Branching occurs based 41 | on explicit test instructions (e.g. `eq`, `lt`, `gt`) that set a test flag. 42 | 43 | Classic *Complex Instruction Set Computing* (CISC) processors could perform 44 | operations directly from memory such as adding a memory value to a special 45 | register called an *accumulator*. The changes to the accumulator would have 46 | side effects like setting a *zero flag* that could be used to determine a 47 | conditional jump. 48 | 49 | CISC was more efficient in the early days as it required fewer instructions to 50 | accomplish the same work. While a CISC design would have been more representative 51 | of early processors, they're harder to understand and not representative of 52 | how modern CPUs work. 53 | 54 | ### Why is the code section in assembly called `.text`? 55 | 56 | This is a fun bit of history that has carried all the way into modern assemblers. When 57 | assembly was first created, *code* was a reference to *machine code*—the bytes of data 58 | executed by the CPU. This came from the idea that computer instructions had to be 59 | *encoded* as binary numbers the computer could understand. 60 | 61 | Assembly Language was created as a textual form of machine code and was thus referred 62 | to as "text" rather than "code". Over time the term "code" shifted to refer to any 63 | representation of software. Including higher level languages like C, Java, Python, 64 | and many others. 65 | 66 | 67 | ### Where is the operating system in memory? 68 | 69 | Early computers kept the operating system in *Read Only Memory* (ROM) that was 70 | not intended to be accessed by end-user programs. The MTMC-16 keeps up this 71 | tradition by providing basic services like console, filesystem, and display without 72 | taking up valuable *Random Access Memory* (RAM) reserved for end-user programs. 73 | 74 | ### How many cycles does each instruction take? 75 | 76 | In real world computers the execution of an instruction typically takes more 77 | than one clock cycle. Knowing how many cycles each instruction takes is important 78 | for *cycle counting*, a practice that was critical in classic game consoles like 79 | the Atari 2600 and Gameboy. 80 | 81 | The purpose of cycle counting was to ensure that the program would complete 82 | game updates and drawing operations before the next frame needed to be rendered. 83 | -------------------------------------------------------------------------------- /src/main/java/mtmc/emulator/MTMCConsole.java: -------------------------------------------------------------------------------- 1 | package mtmc.emulator; 2 | 3 | import java.io.Console; 4 | 5 | import static mtmc.emulator.MTMCConsole.Mode.*; 6 | import mtmc.os.shell.Shell; 7 | import mtmc.tokenizer.MTMCScanner; 8 | import mtmc.tokenizer.MTMCToken; 9 | 10 | public class MTMCConsole { 11 | 12 | Mode mode = NON_INTERACTIVE; 13 | Console sysConsole = null; 14 | private final MonTanaMiniComputer computer; 15 | 16 | // non-interactive data 17 | private StringBuffer output = new StringBuffer(); 18 | private boolean shortValueSet; 19 | private short shortValue; 20 | private String stringValue; 21 | 22 | public MTMCConsole(MonTanaMiniComputer computer) { 23 | this.computer = computer; 24 | } 25 | 26 | // TODO invert so shell is driving and console is just IO 27 | public void start() { 28 | mode = INTERACTIVE; 29 | sysConsole = System.console(); 30 | Shell.printShellWelcome(computer); 31 | while(true) { 32 | String cmd = sysConsole.readLine("mtmc > "); 33 | computer.getOS().processCommand(cmd); 34 | } 35 | } 36 | 37 | public void println(String x) { 38 | print(x); 39 | print("\n"); 40 | } 41 | 42 | public void print(String x) { 43 | output.append(x); 44 | if(mode == INTERACTIVE) { 45 | System.out.print(x); 46 | } else { 47 | if (x.contains("\n")) { 48 | computer.notifyOfConsoleUpdate(); 49 | } else { 50 | computer.notifyOfConsolePrinting(); 51 | } 52 | } 53 | } 54 | 55 | public char readChar() { 56 | if (mode == INTERACTIVE) { 57 | var tokens = new MTMCScanner(sysConsole.readLine(), null).tokenize(); 58 | var token = tokens.getFirst(); 59 | assert token.type() == MTMCToken.TokenType.CHAR; 60 | return token.charValue(); 61 | } else { 62 | this.shortValueSet = false; 63 | return (char) this.shortValue; 64 | } 65 | } 66 | 67 | public short readInt() { 68 | if (mode == INTERACTIVE) { 69 | return Short.parseShort(sysConsole.readLine()); 70 | } else { 71 | this.shortValueSet = false; 72 | return shortValue; 73 | } 74 | } 75 | 76 | public boolean hasShortValue() { 77 | return (mode == INTERACTIVE || shortValueSet); 78 | } 79 | 80 | public void setShortValue(short shortValue) { 81 | this.shortValue = shortValue; 82 | this.shortValueSet = true; 83 | } 84 | 85 | public void setCharValue(char charValue) { 86 | this.shortValue = (short) charValue; 87 | this.shortValueSet = true; 88 | } 89 | 90 | public String getOutput() { 91 | return output.toString(); 92 | } 93 | 94 | public String consumeLines() { 95 | int index = output.lastIndexOf("\n"); 96 | String text = (index >= 0) ? output.substring(0, index+1) : ""; 97 | 98 | if (index >= 0) { 99 | output.delete(0, index+1); 100 | } 101 | 102 | return text; 103 | } 104 | 105 | public void writeInt(short value) { 106 | print(value + ""); 107 | } 108 | 109 | public void setStringValue(String stringValue) { 110 | this.stringValue = stringValue; 111 | } 112 | 113 | public String readString() { 114 | if(mode == INTERACTIVE) { 115 | return sysConsole.readLine(); 116 | } else { 117 | String stringValue = this.stringValue; 118 | this.stringValue = null; 119 | return stringValue; 120 | } 121 | } 122 | 123 | public boolean hasReadString() { 124 | return (mode == INTERACTIVE || stringValue != null); 125 | } 126 | 127 | public void setReadString(String stringValue) { 128 | this.stringValue = stringValue; 129 | } 130 | 131 | public void resetOutput() { 132 | output.delete(0, output.length()); 133 | } 134 | 135 | public enum Mode { 136 | NON_INTERACTIVE, 137 | INTERACTIVE, 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/mtmc/asm/instructions/LoadStoreInstruction.java: -------------------------------------------------------------------------------- 1 | package mtmc.asm.instructions; 2 | 3 | import mtmc.asm.Assembler; 4 | import mtmc.emulator.Register; 5 | import mtmc.tokenizer.MTMCToken; 6 | 7 | import java.util.List; 8 | 9 | import static mtmc.util.BinaryUtils.getBits; 10 | 11 | public class LoadStoreInstruction extends Instruction { 12 | 13 | private MTMCToken targetToken; 14 | private MTMCToken offsetToken; 15 | private MTMCToken value; 16 | 17 | public LoadStoreInstruction(InstructionType type, List label, MTMCToken instructionToken) { 18 | super(type, label, instructionToken); 19 | } 20 | 21 | public void setTargetToken(MTMCToken targetToken) { 22 | this.targetToken = targetToken; 23 | } 24 | 25 | public void setOffsetToken(MTMCToken offsetToken) { 26 | this.offsetToken = offsetToken; 27 | } 28 | 29 | public void setValue(MTMCToken value) { 30 | this.value = value; 31 | } 32 | 33 | public boolean isOffset() { 34 | return getType().name().endsWith("O"); 35 | } 36 | 37 | @Override 38 | public void validateLabel(Assembler assembler) { 39 | if (value.type() == MTMCToken.TokenType.IDENTIFIER) { 40 | if (!assembler.hasLabel(value.stringValue())) { 41 | addError("Unresolved label: " + value.stringValue()); 42 | } 43 | } 44 | } 45 | 46 | private Integer resolveValue(Assembler assembler) { 47 | if (value.type() == MTMCToken.TokenType.IDENTIFIER) { 48 | return assembler.resolveLabel(value.stringValue()); 49 | } else { 50 | return value.intValue(); 51 | } 52 | } 53 | 54 | @Override 55 | public void genCode(byte[] output, Assembler assembler) { 56 | int upperByte = switch (getType()) { 57 | case LW -> 0b1000_0000; 58 | case LWO -> 0b1000_0001; 59 | case LB -> 0b1000_0010; 60 | case LBO -> 0b1000_0011; 61 | case SW -> 0b1000_0100; 62 | case SWO -> 0b1000_0101; 63 | case SB -> 0b1000_0110; 64 | case SBO -> 0b1000_0111; 65 | case LI -> 0b1000_1111; 66 | default -> 0; 67 | }; 68 | 69 | int target = Register.toInteger(targetToken.stringValue()); 70 | output[getLocation()] = (byte) upperByte; 71 | 72 | if (isOffset()) { 73 | int offsetReg = Register.toInteger(offsetToken.stringValue()); 74 | output[getLocation() + 1] = (byte) (target << 4 | offsetReg); 75 | } else { 76 | output[getLocation() + 1] = (byte) (target << 4); 77 | } 78 | 79 | int numericValue = resolveValue(assembler); 80 | output[getLocation() + 2] = (byte) (numericValue >>> 8); 81 | output[getLocation() + 3] = (byte) numericValue; 82 | 83 | } 84 | 85 | public static String disassemble(short instruction) { 86 | if (getBits(16, 4, instruction) == 0b1000) { 87 | short topNibble = getBits(12, 4, instruction); 88 | StringBuilder sb = new StringBuilder(); 89 | if (topNibble == 0b1111) { 90 | sb.append("li "); 91 | } else if (topNibble == 0b000) { 92 | sb.append("lw "); 93 | } else if (topNibble == 0b001) { 94 | sb.append("lwo "); 95 | } else if (topNibble == 0b010) { 96 | sb.append("lb "); 97 | } else if (topNibble == 0b011) { 98 | sb.append("lbo "); 99 | } else if (topNibble == 0b100) { 100 | sb.append("sw "); 101 | } else if (topNibble == 0b101) { 102 | sb.append("swo "); 103 | } else if (topNibble == 0b110) { 104 | sb.append("sb "); 105 | } else if (topNibble == 0b111) { 106 | sb.append("sbo "); 107 | } 108 | short target = getBits(8, 4, instruction); 109 | String reg = Register.fromInteger(target); 110 | sb.append(reg); 111 | if (topNibble == 0b001 || topNibble == 0b011 || topNibble == 0b101 || topNibble == 0b111) { 112 | short offset = getBits(4, 4, instruction); 113 | String offsetReg = Register.fromInteger(offset); 114 | sb.append(" ").append(offsetReg); 115 | } 116 | return sb.toString(); 117 | } 118 | return null; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /disk/bin/ls: -------------------------------------------------------------------------------- 1 | {"format":"Orc1","code":[56,96,-124,96,0,-90,-32,46,1,6,-113,16,1,-60,-128,32,1,-62,-124,0,0,-90,-125,48,0,0,-121,49,0,0,2,1,2,17,3,33,56,48,-32,56,56,32,-48,18,-64,56,-113,96,1,-60,-128,112,1,-62,0,18,-113,96,1,-60,-113,112,0,0,0,20,-124,-96,0,-92,-128,0,0,-92,-113,16,-1,-1,48,1,-32,-108,56,0,-32,-94,-113,0,0,0,-113,96,1,-60,-113,112,0,1,1,-128,-113,-112,0,-66,0,20,-113,96,0,-62,0,6,-113,32,0,1,-128,48,0,-66,48,35,-48,-126,-113,96,0,-86,0,6,-113,96,0,-88,0,6,2,1,-128,16,0,-92,48,1,-48,90,-64,-94,-113,96,0,-84,-113,112,1,0,-113,48,0,-84,0,6,0,0],"data":[-1,-1,-1,-1,10,0,47,0,73,78,86,65,76,73,68,32,68,73,82,69,67,84,79,82,89,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"graphics":[],"sourceName":"/src/utils/ls.asm","debugInfo":{"debugStrings":[],"assemblyFile":"/src/utils/ls.asm","assemblySource":".data\n count: -1\n path: -1\n newline: \"\\n\"\n slash: \"/\"\n invalid_dir: \"INVALID DIRECTORY\"\n\nfile:\n # Output Directory Entry\n file_flags: 0\n file_name_size: 256\n file_name: .byte 256\n\ndir:\n # Input Directory\n dir_name_size: 256\n dir_name: .byte 256\n\n\n.text\nmain:\n # Check if a path was passed\n eqi a0 0\n sw a0 path\n jnz get_cwd\n\n # Copy the passed in name to dir_name\n mov t0 a0\n li t1 dir_name\n lw t2 dir_name_size\n\nloop_dir_load:\n sw t0 path\n lbo t3 t0 0\n sbo t3 t1 0\n\n inc t0\n inc t1\n dec t2\n\n # NULL terminator found\n eqi t3 0\n jnz start\n\n # Check max size\n eqi t2 0\n jz loop_dir_load\n\n j start\n\nget_cwd:\n # Get current working directory\n li a0 dir_name\n lw a1 dir_name_size\n\n sys cwd\n\nstart:\n li a0 dir_name\n li a1 0\n \n sys dirent\n\n sw rv count\n lw t0 count\n li t1 -1\n eq t0 t1\n jnz print_invalid_dir\n\n # If no files, jump to end\n eqi t0 0\n jnz done\n\n # loop over files\n li t0 0\n\nloop_file_list:\n li a0 dir_name\n li a1 1\n mov a2 t0\n li a3 file\n\n sys dirent\n\n li a0 file_name\n sys wstr\n\n # Print / if directory\n li t2 1\n lw t3 file_flags\n eq t2 t3\n jz print_newline\n\n li a0 slash\n sys wstr\n\nprint_newline:\n li a0 newline\n sys wstr\n \n inc t0\n lw t1 count\n eq t0 t1\n jz loop_file_list\n \n j done\n\nprint_invalid_dir:\n li a0 invalid_dir\n li a1 256\n li t3 invalid_dir\n sys wstr\n \ndone:\n sys exit\n","assemblyLineNumbers":[23,23,24,24,24,24,25,25,28,28,29,29,29,29,30,30,30,30,33,33,33,33,34,34,34,34,35,35,35,35,37,37,38,38,39,39,42,42,43,43,46,46,47,47,49,49,53,53,53,53,54,54,54,54,56,56,59,59,59,59,60,60,60,60,62,62,64,64,64,64,65,65,65,65,66,66,66,66,67,67,68,68,71,71,72,72,75,75,75,75,78,78,78,78,79,79,79,79,80,80,81,81,81,81,83,83,85,85,85,85,86,86,89,89,89,89,90,90,90,90,91,91,92,92,94,94,94,94,95,95,98,98,98,98,99,99,101,101,102,102,102,102,103,103,104,104,106,106,109,109,109,109,110,110,110,110,111,111,111,111,112,112,115,115],"originalFile":"","originalLineNumbers":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"globals":[],"locals":[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]}} --------------------------------------------------------------------------------