initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
states/
|
||||||
|
vm
|
||||||
15
Makefile
Normal file
15
Makefile
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Makefile for the C-based VM project
|
||||||
|
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -std=c11 -Wall -Wextra -Iinclude
|
||||||
|
TARGET = vm
|
||||||
|
SOURCES = src/*.c
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET):
|
||||||
|
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCES)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGET)
|
||||||
|
|
||||||
260
README.md
Normal file
260
README.md
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
# OpenAIcVM
|
||||||
|
|
||||||
|
**OpenAIcVM** is an advanced C-based Virtual Machine (VM) with a fully featured, nested operating system (OS) shell. This project was created as a demonstration of the capabilities of modern AI-assisted coding—showing how iterative, detailed prompts can lead to the development of a complex, modular software system from scratch.
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
Much to my embarressment, my bio is no longer true. Sadly, not all of this code was created by humans. It was created on earth, but this ungodly, unhuman creation was created by the gross, monolithic amalgamation algothm with a hat that tries to pretend to be smart and that is known as ChatGPT-03-mini-high.
|
||||||
|
I feel this disclaimer is warranted so I can in good contious still have my bio stay the same as it currently is which is (incase I ever change it in the future): "All code made on earth, by humans"
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
OpenAIcVM supports a custom instruction set that includes arithmetic operations, branching, stack management, function calls/returns, and transitions into an interactive nested OS shell. The OS shell simulates a simple file system, supports commands such as `ls`, `mkdir`, `touch`, `cd`, `edit`, `cat`, `cp`, and even includes a "save" command to persist the current state. Additional features include:
|
||||||
|
- **Autosave on termination:** The VM automatically saves the OS state (into a custom `.osstate` file stored in a `states` directory) when receiving termination signals (e.g., Control‑C).
|
||||||
|
- **Reload capability:** Start the VM with a `--reload <path>` option to load a previously saved state.
|
||||||
|
- **Control‑L support:** Typing Control‑L (or its equivalent input) clears the OS shell screen.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
The main purpose of OpenAIcVM is to test and showcase the capabilities of a coding AI - ChatGPT o3-mini-high. In this project:
|
||||||
|
- Detailed, iterative prompts were used to guide the design and implementation.
|
||||||
|
- New features and improvements were added progressively, illustrating how an AI can respond to and incorporate feedback.
|
||||||
|
- The project highlights modular design principles, robust error handling, and advanced functionality using only standard C (C11).
|
||||||
|
|
||||||
|
## Conversation Summary and Analysis
|
||||||
|
|
||||||
|
Throughout our conversation, the user guided the project with a series of detailed prompts:
|
||||||
|
- **Initial VM design:** Requests for a modular VM with a custom instruction set.
|
||||||
|
- **Nested OS Shell:** Enhancements included creating an interactive shell with simulated file system operations.
|
||||||
|
- **Feature Extensions:** Additional commands were added (such as file copying, text editing, and autosave on termination), and improvements were made (e.g., handling Control‑L for screen clearing).
|
||||||
|
- **State Persistence:** The VM was enhanced to support saving and reloading its state via a custom file format.
|
||||||
|
- **Meta Discussion:** Finally, the user requested a README to explain the project purpose and reflect on the iterative process.
|
||||||
|
|
||||||
|
This process illustrates the potential of AI-assisted programming. While internal conversation details and prompts remain confidential, the evolution of OpenAIcVM—from a simple virtual machine to a feature-rich system—is a testament to the dynamic interaction between user guidance and AI-generated code.
|
||||||
|
|
||||||
|
## Build and Run Instructions
|
||||||
|
|
||||||
|
To compile the project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the VM:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./vm [--reload <path-to-state-file>]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
project/
|
||||||
|
├── include/
|
||||||
|
│ ├── opcodes.h // Definition of the custom instruction set.
|
||||||
|
│ ├── os.h // Interface for the nested OS shell and state management.
|
||||||
|
│ └── vm.h // VM structure and function prototypes.
|
||||||
|
└── src/
|
||||||
|
├── main.c // Main entry point, signal handling, and program loop.
|
||||||
|
├── os.c // Implementation of the nested OS shell and file system.
|
||||||
|
└── vm.c // Implementation of the virtual machine.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prompts used:
|
||||||
|
- Start Prompt (1):
|
||||||
|
```txt
|
||||||
|
You are an advanced AI with chain-of-thought reasoning. Your task is to create a more advanced, fully-fledged C-based Virtual Machine from scratch. Do not hallucinate or invent libraries/functions that don’t exist in standard C. Ensure error-free code that strictly follows the C11 (or newer) standard and compiles cleanly under a typical C compiler (gcc/clang) with -Wall -Wextra flags. Provide only the source code, with inline explanations and build instructions. Do not create a README.
|
||||||
|
Requirements
|
||||||
|
|
||||||
|
Modular Project Structure
|
||||||
|
Create a top-level project/ directory.
|
||||||
|
Inside project/, have include/ for headers and src/ for .c files.
|
||||||
|
Provide separate .h and .c files with robust inline comments.
|
||||||
|
|
||||||
|
Advanced Virtual Machine Design
|
||||||
|
Implement a custom instruction set that is more extensive than simple LOAD/PRINT/HALT.
|
||||||
|
Include arithmetic instructions (e.g., ADD, SUB, MUL, DIV), branching (e.g., JMP, JMPZ), stack operations (PUSH, POP), and function calls/returns if you wish.
|
||||||
|
Manage registers and memory safely; avoid undefined behavior.
|
||||||
|
Make sure the VM is capable of interpreting bytecode that you define.
|
||||||
|
Provide a well-commented example bytecode program demonstrating these new instructions.
|
||||||
|
|
||||||
|
Nested OS / Shell
|
||||||
|
Demonstrate a more feature-rich nested OS or shell that can be entered via an instruction (e.g., OP_CALL_OS).
|
||||||
|
Implement at least one or two advanced capabilities in the nested OS (for example, a mini file system simulation in memory, or a small command set that manipulates virtual memory).
|
||||||
|
Keep it simple but functional—no need for actual disk I/O, but show how it could be extended.
|
||||||
|
|
||||||
|
Safety & No Extraneous Dependencies
|
||||||
|
Write safe, portable C code.
|
||||||
|
No external libraries beyond the C standard library.
|
||||||
|
Use checks and error messages for invalid registers, memory bounds, or unexpected instruction opcodes.
|
||||||
|
|
||||||
|
Inline Documentation & Build/Run Instructions
|
||||||
|
No README—instead, provide a concise set of instructions in your final answer (e.g., compile with gcc -std=c11 -Wall -Wextra -o vm src/*.c).
|
||||||
|
Explain the purpose of each file, instruction set design, and how to run the example program within the final answer.
|
||||||
|
Show that it is genuinely capable of running the advanced instructions and the nested OS.
|
||||||
|
|
||||||
|
Deliverables
|
||||||
|
Present all source code for the project in your final response (i.e., contents of each file in include/ and src/).
|
||||||
|
Include a single example bytecode program that demonstrates:
|
||||||
|
Arithmetic, branching, stack usage, function calls (if implemented), or any other advanced instructions you have added.
|
||||||
|
A transition into and out of the nested OS/shell.
|
||||||
|
Indicate how to compile (exact command) and run the resulting VM.
|
||||||
|
|
||||||
|
Make sure your final output is self-contained and error-free. Do not hallucinate. Only standard C code, minimal shell commands, and inline documentation. No README.
|
||||||
|
```
|
||||||
|
|
||||||
|
- secondary Follow Up Prompt (2):
|
||||||
|
```txt
|
||||||
|
This is the program it created: (ins)klein@kernelpanic:~/codeWS/C/OpenAIcVM$ ./vm
|
||||||
|
Register 0: 30
|
||||||
|
Register 2: 5
|
||||||
|
Register 0: 60
|
||||||
|
Entering Nested OS Shell. Type 'exit' to return to VM.
|
||||||
|
nested-os> help
|
||||||
|
Available commands: help, exit, echo <text>
|
||||||
|
nested-os> echo lol
|
||||||
|
lol
|
||||||
|
nested-os> exit
|
||||||
|
Exiting Nested OS Shell.
|
||||||
|
Error: Stack underflow in OP_RET. I would like more options like basic gnu utilities, make it a bit more advanced, do not use actual gnu utilities but like make all of this from scratch in your own mind. Additionally fix the stack underflow error, possible add more registers and memory and such to accomodate this. It should literally be an OS that you emulate from scratch but it should be able to actually do something beyond those 3 options, for instance make files, make directories, move them, remove them, have a very simple shell, accept system calls like up arrows and stuff.
|
||||||
|
```
|
||||||
|
|
||||||
|
- third follow up prompt (3):
|
||||||
|
```txt
|
||||||
|
This is the new output: (ins)klein@kernelpanic:~/codeWS/C/OpenAIcVM$ ./vm
|
||||||
|
Register 0: 30
|
||||||
|
Register 2: 5
|
||||||
|
Register 0: 60
|
||||||
|
Entering Nested OS Shell. Type 'exit' to return to VM.
|
||||||
|
nested-os> help
|
||||||
|
Available commands:
|
||||||
|
help - Show this help message.
|
||||||
|
exit - Exit the nested OS shell.
|
||||||
|
ls - List directory contents.
|
||||||
|
mkdir <name> - Create a new directory.
|
||||||
|
rmdir <name> - Remove an empty directory.
|
||||||
|
touch <name> - Create a new file.
|
||||||
|
rm <name> - Remove a file.
|
||||||
|
cd <name> - Change directory (.. for parent, / for root).
|
||||||
|
pwd - Print working directory.
|
||||||
|
history - Show command history.
|
||||||
|
echo <text> - Print text.
|
||||||
|
cat <name> - Show file contents (dummy content).
|
||||||
|
nested-os> ls
|
||||||
|
Contents of /:
|
||||||
|
nested-os> pwd
|
||||||
|
Unknown command. Type 'help' for available commands.
|
||||||
|
nested-os> pwd
|
||||||
|
/
|
||||||
|
nested-os> touch test
|
||||||
|
nested-os> ls
|
||||||
|
Contents of /:
|
||||||
|
FILE test
|
||||||
|
nested-os> cat test
|
||||||
|
Contents of test: [This is a dummy file.]
|
||||||
|
nested-os> . Please only give me the files you change but keep reference to all of the files youve made. I would like the following iprovements: 1) imporove cat to actually show the contents of a file, 2) add a cp command, 3) add a text editor. All of this should be your own creation and your own code. Make the operating system more interactive, more realistic, have a real shell that you make up.
|
||||||
|
```
|
||||||
|
|
||||||
|
- Fourth follow up prompt (4):
|
||||||
|
```txt
|
||||||
|
With that new os.c I now get: (ins)klein@kernelpanic:~/codeWS/C/OpenAIcVM$ make
|
||||||
|
gcc -std=c11 -Wall -Wextra -Iinclude -o vm src/*.c
|
||||||
|
/usr/bin/ld: /tmp/ccTEcE5I.o: in function run_os':
|
||||||
|
vm.c:(.text+0xb45): multiple definition of run_os'; /tmp/ccvPItXb.o:os.c:(.text+0xa3f): first defined here
|
||||||
|
/usr/bin/ld: /tmp/ccTC61kR.o: in function main':
|
||||||
|
main.c:(.text+0x16): undefined reference to init_vm'
|
||||||
|
/usr/bin/ld: main.c:(.text+0x1d5): undefined reference to load_program'
|
||||||
|
/usr/bin/ld: main.c:(.text+0x1ef): undefined reference to run_vm'
|
||||||
|
collect2: error: ld returned 1 exit status
|
||||||
|
make: *** [Makefile:11: vm] Error 1 also make sure that the main.c handles control Cs or Ds to properly kill itself and clean itself.
|
||||||
|
```
|
||||||
|
|
||||||
|
- Fifth follow up prompt - Ending Prompt (5):
|
||||||
|
```txt
|
||||||
|
Okay thats perfect it compiles. I have one last request for the shell. I would like to add support for control L to clear the inside screen (ie trap it), and add 1 more command and add the supporting functionality to "save" the operating system and the state that the user left it in. Like add a flag to the vm like --reload <path to file>. Make a custom file type for this save like entirely your own idea. Additionally change the control C functionality to autosave as well and by default it should save and make a directory called states where it should save the custom os file type that you make.
|
||||||
|
```
|
||||||
|
|
||||||
|
## LICENSE
|
||||||
|
|
||||||
|
Fucking chatgpt wrote this entire program except for the readme. Fuck OpenAI but this model is pretty impressive.
|
||||||
|
Do whatever the fuck you want with this code. There is no need for a license agreement bc this isn't mine by nature.
|
||||||
|
As god is my witness I am sure this repo will somehow indept my soul to OpenAI and I am sure they will come to collect.
|
||||||
|
Propriatary software - especially that not made by a human is fucking stupid. You heard it here first.
|
||||||
|
|
||||||
|
|
||||||
|
## Some Beautiful ASCII text to express my love of OpenAI
|
||||||
|
```txt
|
||||||
|
______ _ ____ _____
|
||||||
|
| ____| | | / __ \ /\ |_ _|
|
||||||
|
| |__ _ _ ___| | __ | | | |_ __ ___ _ __ / \ | |
|
||||||
|
| __| | | |/ __| |/ / | | | | '_ \ / _ \ '_ \ / /\ \ | |
|
||||||
|
| | | |_| | (__| < | |__| | |_) | __/ | | |/ ____ \ _| |_
|
||||||
|
|_| \__,_|\___|_|\_\ \____/| .__/ \___|_| |_/_/ \_\_____|
|
||||||
|
| |
|
||||||
|
|_|
|
||||||
|
```
|
||||||
|
|
||||||
|
```txt
|
||||||
|
____ _____ _ _____ _ _ _
|
||||||
|
/ __ \ /\ |_ _| (_) | __ \ (_) | | |
|
||||||
|
| | | |_ __ ___ _ __ / \ | | _ ___ | |__) |___ ___ _ __ ___ _ __ ___ _| |__ | | ___
|
||||||
|
| | | | '_ \ / _ \ '_ \ / /\ \ | | | / __| | _ // _ \/ __| '_ \ / _ \| '_ \/ __| | '_ \| |/ _ \
|
||||||
|
| |__| | |_) | __/ | | |/ ____ \ _| |_ | \__ \ | | \ \ __/\__ \ |_) | (_) | | | \__ \ | |_) | | __/
|
||||||
|
\____/| .__/ \___|_| |_/_/ \_\_____| |_|___/ |_| _\_\___||___/ .__/ \___/|_| |_|___/_|_.__/|_|\___|
|
||||||
|
| ____| | | | | | | __ \ | | | | | | / _| (_) |
|
||||||
|
| |__ _|_| _ __ | |_| |__ ___ | | | | ___ __ _| |_| |__ |_|_ | |_ _| |_ ___
|
||||||
|
| __/ _ \| '__| | __| '_ \ / _ \ | | | |/ _ \/ _` | __| '_ \ / _ \| _| | | __/ __|
|
||||||
|
| | | (_) | | | |_| | | | __/ | |__| | __/ (_| | |_| | | | | (_) | | | | |_\__ \
|
||||||
|
|_| \___/|_|__ \__|_| |_|\___| |_____/ \___|\__,_|\__|_| |_| \___/|_| |_|\__|___/
|
||||||
|
\ \ / / | (_) | | | | | | | |
|
||||||
|
\ \ /\ / /| |__ _ ___| |_| | ___ | |__ | | _____ _____ _ __
|
||||||
|
\ \/ \/ / | '_ \| / __| __| |/ _ \ | '_ \| |/ _ \ \ /\ / / _ \ '__|
|
||||||
|
\ /\ / | | | | \__ \ |_| | __/ | |_) | | (_) \ V V / __/ |
|
||||||
|
\/ \/ |_| |_|_|___/\__|_|\___| |_.__/|_|\___/ \_/\_/ \___|_|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```txt
|
||||||
|
_____ _ _ _____ ______ _
|
||||||
|
/ ____| /\ | | | |_ _| | ____| | |
|
||||||
|
| (___ __ _ _ __ ___ / \ | | |_ _ __ ___ __ _ _ __ | | ___ __ _ | |__ _ __ __ _ _ _ __| |
|
||||||
|
\___ \ / _` | '_ ` _ \ / /\ \ | | __| '_ ` _ \ / _` | '_ \ | | / __| / _` | | __| '__/ _` | | | |/ _` |
|
||||||
|
____) | (_| | | | | | | / ____ \| | |_| | | | | | (_| | | | | _| |_\__ \ | (_| | | | | | | (_| | |_| | (_| |
|
||||||
|
|_____/ \__,_|_| |_| |_| /_/ \_\_|\__|_| |_| |_|\__,_|_| |_| |_____|___/ \__,_| |_| |_| \__,_|\__,_|\__,_|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```txt
|
||||||
|
_
|
||||||
|
/\ | |
|
||||||
|
/ \ ___ ___ _ __ ___ _ __ _ _| |_ ___ _ __ ___ __ _ _ __ _ __ _____ _____ _ __
|
||||||
|
/ /\ \ / __/ _ \| '_ ` _ \| '_ \| | | | __/ _ \ '__| / __/ _` | '_ \ | '_ \ / _ \ \ / / _ \ '__|
|
||||||
|
/ ____ \ | (_| (_) | | | | | | |_) | |_| | || __/ | | (_| (_| | | | | | | | | __/\ V / __/ |
|
||||||
|
/_/ \_\ \___\___/|_| |_| |_| .__/ \__,_|\__\___|_| \___\__,_|_| |_| |_| |_|\___| \_/ \___|_|
|
||||||
|
| |
|
||||||
|
_ _ _ |_| _ _ _
|
||||||
|
| | | | | | | | | | | | | |
|
||||||
|
| |__ ___ | |__ ___| | __| | __ _ ___ ___ ___ _ _ _ __ | |_ __ _| |__ | | ___
|
||||||
|
| '_ \ / _ \ | '_ \ / _ \ |/ _` | / _` |/ __/ __/ _ \| | | | '_ \| __/ _` | '_ \| |/ _ \
|
||||||
|
| |_) | __/ | | | | __/ | (_| | | (_| | (_| (_| (_) | |_| | | | | || (_| | |_) | | __/_
|
||||||
|
|_.__/ \___| |_| |_|\___|_|\__,_| \__,_|\___\___\___/ \__,_|_| |_|\__\__,_|_.__/|_|\___( )
|
||||||
|
_ _ __ _ _ _ |/
|
||||||
|
| | | | / _| (_) | | |
|
||||||
|
| |_| |__ ___ _ __ ___| |_ ___ _ __ ___ _| |_ _ __ ___ _ _ ___| |_
|
||||||
|
| __| '_ \ / _ \ '__/ _ \ _/ _ \| '__/ _ \ | | __| | '_ ` _ \| | | / __| __|
|
||||||
|
| |_| | | | __/ | | __/ || (_) | | | __/ | | |_ | | | | | | |_| \__ \ |_
|
||||||
|
\__|_| |_|\___|_| \___|_| \___/|_| \___| |_|\__| |_| |_| |_|\__,_|___/\__|
|
||||||
|
| |
|
||||||
|
_ __ _____ _____ _ __ _ __ ___ __ _| | _____
|
||||||
|
| '_ \ / _ \ \ / / _ \ '__| | '_ ` _ \ / _` | |/ / _ \
|
||||||
|
| | | | __/\ V / __/ | | | | | | | (_| | < __/ _ _ _ _
|
||||||
|
|_| |_|\___| \_/ \___|_| |_| |_| |_|\__,_|_|\_\___| | | | | (_) (_)
|
||||||
|
_ __ ___ __ _ _ __ __ _ __ _ ___ _ __ ___ ___ _ __ | |_ __| | ___ ___ _ ___ _ ___ _ __ ___
|
||||||
|
| '_ ` _ \ / _` | '_ \ / _` |/ _` |/ _ \ '_ ` _ \ / _ \ '_ \| __| / _` |/ _ \/ __| / __| |/ _ \| '_ \/ __|
|
||||||
|
| | | | | | (_| | | | | (_| | (_| | __/ | | | | | __/ | | | |_ | (_| | __/ (__| \__ \ | (_) | | | \__ \
|
||||||
|
|_| |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| |_| |_|\___|_| |_|\__| \__,_|\___|\___|_|___/_|\___/|_| |_|___/
|
||||||
|
__/ |
|
||||||
|
|___/
|
||||||
|
```
|
||||||
24
include/opcodes.h
Normal file
24
include/opcodes.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef OPCODES_H
|
||||||
|
#define OPCODES_H
|
||||||
|
|
||||||
|
// Define opcodes for the Virtual Machine.
|
||||||
|
// Each opcode is an integer constant used by the VM.
|
||||||
|
enum {
|
||||||
|
OP_HALT = 0, // Stop execution.
|
||||||
|
OP_PUSH, // Push an immediate value onto the stack. (Operand: value)
|
||||||
|
OP_POP, // Pop the top of the stack into a register. (Operand: register index)
|
||||||
|
OP_LOAD, // Load an immediate value into a register. (Operands: register, value)
|
||||||
|
OP_ADD, // Add two registers; store the result in the first register. (Operands: reg_dest, reg_src)
|
||||||
|
OP_SUB, // Subtract second register from first; store result in the first register. (Operands: reg_dest, reg_src)
|
||||||
|
OP_MUL, // Multiply two registers; store result in the first register. (Operands: reg_dest, reg_src)
|
||||||
|
OP_DIV, // Divide first register by second; store result in the first register. (Operands: reg_dest, reg_src)
|
||||||
|
OP_PRINT, // Print the value of a register. (Operand: register index)
|
||||||
|
OP_JMP, // Unconditional jump to a specified program address. (Operand: address)
|
||||||
|
OP_JMPZ, // Jump to a specified address if the given register is zero. (Operands: register, address)
|
||||||
|
OP_CALL, // Call a function: push return address and jump. (Operand: address)
|
||||||
|
OP_RET, // Return from a function call. (No operand)
|
||||||
|
OP_CALL_OS // Enter the nested OS shell. (No operand)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // OPCODES_H
|
||||||
|
|
||||||
15
include/os.h
Normal file
15
include/os.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef OS_H
|
||||||
|
#define OS_H
|
||||||
|
|
||||||
|
// Declare the global reload_path so that it can be used in other files.
|
||||||
|
extern char reload_path[256];
|
||||||
|
|
||||||
|
// Declaration for the nested OS shell.
|
||||||
|
void run_os(void);
|
||||||
|
|
||||||
|
// Functions to save and load the OS state (the in-memory file system).
|
||||||
|
// The state is saved in a custom file type (".osstate").
|
||||||
|
int save_state(const char *filename);
|
||||||
|
int load_state(const char *filename);
|
||||||
|
|
||||||
|
#endif // OS_H
|
||||||
27
include/vm.h
Normal file
27
include/vm.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef VM_H
|
||||||
|
#define VM_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// Increase registers and memory sizes for advanced programs.
|
||||||
|
#define NUM_REGISTERS 16 // Number of general-purpose registers.
|
||||||
|
#define STACK_SIZE 512 // Maximum stack size.
|
||||||
|
#define PROGRAM_SIZE 2048 // Maximum program size (number of ints).
|
||||||
|
|
||||||
|
// VM structure definition.
|
||||||
|
typedef struct VM {
|
||||||
|
int registers[NUM_REGISTERS]; // General purpose registers.
|
||||||
|
int stack[STACK_SIZE]; // Stack memory.
|
||||||
|
int sp; // Stack pointer (index of next free slot).
|
||||||
|
int program[PROGRAM_SIZE]; // Bytecode program memory.
|
||||||
|
size_t program_size; // Number of ints in the loaded program.
|
||||||
|
size_t pc; // Program counter.
|
||||||
|
} VM;
|
||||||
|
|
||||||
|
// Function prototypes for the VM.
|
||||||
|
void init_vm(VM *vm);
|
||||||
|
int load_program(VM *vm, const int *program, size_t size);
|
||||||
|
void run_vm(VM *vm);
|
||||||
|
|
||||||
|
#endif // VM_H
|
||||||
|
|
||||||
76
src/main.c
Normal file
76
src/main.c
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "vm.h"
|
||||||
|
#include "opcodes.h"
|
||||||
|
#include "os.h"
|
||||||
|
|
||||||
|
// Global flags for termination and autosave request.
|
||||||
|
volatile sig_atomic_t stop = 0;
|
||||||
|
volatile sig_atomic_t autosave_requested = 0;
|
||||||
|
|
||||||
|
// Signal handler to catch SIGINT/SIGTERM.
|
||||||
|
// Sets flags so that main() can perform an autosave.
|
||||||
|
void handle_signal(int signum) {
|
||||||
|
stop = 1;
|
||||||
|
autosave_requested = 1;
|
||||||
|
printf("\nSignal %d received. Autosaving state and shutting down VM...\n", signum);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// Check for the --reload flag.
|
||||||
|
if (argc >= 3 && strcmp(argv[1], "--reload") == 0) {
|
||||||
|
strncpy(reload_path, argv[2], sizeof(reload_path) - 1);
|
||||||
|
reload_path[sizeof(reload_path)-1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register signal handlers.
|
||||||
|
signal(SIGINT, handle_signal);
|
||||||
|
signal(SIGTERM, handle_signal);
|
||||||
|
|
||||||
|
VM vm;
|
||||||
|
init_vm(&vm);
|
||||||
|
|
||||||
|
int program[] = {
|
||||||
|
OP_LOAD, 0, 10, // 0: R0 = 10
|
||||||
|
OP_LOAD, 1, 20, // 3: R1 = 20
|
||||||
|
OP_ADD, 0, 1, // 6: R0 = R0 + R1 => 30
|
||||||
|
OP_PRINT, 0, // 9: Print R0 (30)
|
||||||
|
OP_PUSH, 5, // 11: Push 5 onto the stack
|
||||||
|
OP_POP, 2, // 13: Pop into R2 (5)
|
||||||
|
OP_PRINT, 2, // 15: Print R2 (5)
|
||||||
|
OP_JMPZ, 2, 21, // 17: If R2 == 0, jump to address 21 (won't jump)
|
||||||
|
OP_CALL, 34, // 20: Call function at address 34
|
||||||
|
OP_PRINT, 0, // 22: Print R0 (updated by function)
|
||||||
|
OP_CALL_OS, // 24: Enter nested OS shell
|
||||||
|
OP_HALT, // 25: Halt execution
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, // Addresses 26-33: filler
|
||||||
|
OP_LOAD, 3, 2, // 34: Function: load R3 with 2
|
||||||
|
OP_MUL, 0, 3, // 37: Function: R0 = R0 * R3 (30*2 = 60)
|
||||||
|
OP_RET // 40: Return from function call
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t program_size = sizeof(program) / sizeof(program[0]);
|
||||||
|
if (load_program(&vm, program, program_size) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the VM. The loop allows the program to be interrupted.
|
||||||
|
while (!stop) {
|
||||||
|
run_vm(&vm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an autosave was requested (via Ctrl-C), save the OS state.
|
||||||
|
if (autosave_requested) {
|
||||||
|
const char *savefile = "states/save.osstate";
|
||||||
|
if (save_state(savefile) == 0) {
|
||||||
|
printf("Autosaved state to %s\n", savefile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("VM terminated.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
448
src/os.c
Normal file
448
src/os.c
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "os.h"
|
||||||
|
|
||||||
|
#define INPUT_SIZE 128
|
||||||
|
#define FILE_CONTENT_SIZE 1024
|
||||||
|
|
||||||
|
// ----- In-Memory File System Definitions -----
|
||||||
|
#define MAX_FS_NODES 64 // Maximum number of file system nodes.
|
||||||
|
#define MAX_CHILDREN 16 // Maximum children per directory.
|
||||||
|
#define NAME_SIZE 32 // Maximum length for file/directory names.
|
||||||
|
|
||||||
|
typedef enum { FS_DIR, FS_FILE } fs_type_t;
|
||||||
|
|
||||||
|
typedef struct fs_node {
|
||||||
|
char name[NAME_SIZE]; // Node name.
|
||||||
|
fs_type_t type; // FS_DIR or FS_FILE.
|
||||||
|
int parent; // Parent index (-1 for root).
|
||||||
|
int children[MAX_CHILDREN]; // Children indices (if directory).
|
||||||
|
int num_children; // Number of children.
|
||||||
|
char content[FILE_CONTENT_SIZE]; // File content (empty for directories).
|
||||||
|
} fs_node_t;
|
||||||
|
|
||||||
|
// Global variables for file system state.
|
||||||
|
static fs_node_t fs_nodes[MAX_FS_NODES];
|
||||||
|
static int fs_node_count = 0;
|
||||||
|
static int current_dir = 0; // Current directory index.
|
||||||
|
|
||||||
|
// A flag to indicate whether the OS state has been initialized.
|
||||||
|
static int os_state_initialized = 0;
|
||||||
|
|
||||||
|
// Global variable for reload path (if provided via command-line).
|
||||||
|
// If non-empty, run_os will attempt to load state from it.
|
||||||
|
char reload_path[256] = "";
|
||||||
|
|
||||||
|
// Initialize the file system with a root directory.
|
||||||
|
static void fs_init(void) {
|
||||||
|
fs_node_count = 0;
|
||||||
|
current_dir = 0;
|
||||||
|
strncpy(fs_nodes[0].name, "/", NAME_SIZE);
|
||||||
|
fs_nodes[0].type = FS_DIR;
|
||||||
|
fs_nodes[0].parent = -1;
|
||||||
|
fs_nodes[0].num_children = 0;
|
||||||
|
fs_nodes[0].content[0] = '\0';
|
||||||
|
for (int i = 0; i < MAX_CHILDREN; i++) {
|
||||||
|
fs_nodes[0].children[i] = -1;
|
||||||
|
}
|
||||||
|
fs_node_count = 1;
|
||||||
|
os_state_initialized = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the current OS state (file system) to a file with our custom format.
|
||||||
|
// The file begins with a magic header "OSSTATE" (7 bytes), followed by version (int),
|
||||||
|
// then fs_node_count, current_dir, and the entire fs_nodes array.
|
||||||
|
int save_state(const char *filename) {
|
||||||
|
// Ensure the "states" directory exists.
|
||||||
|
struct stat st = {0};
|
||||||
|
if (stat("states", &st) == -1) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
_mkdir("states");
|
||||||
|
#else
|
||||||
|
if (mkdir("states", 0755) == -1) {
|
||||||
|
perror("mkdir");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *f = fopen(filename, "wb");
|
||||||
|
if (!f) {
|
||||||
|
perror("fopen for save_state");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const char header[7] = "OSSTATE";
|
||||||
|
fwrite(header, sizeof(char), 7, f);
|
||||||
|
int version = 1;
|
||||||
|
fwrite(&version, sizeof(int), 1, f);
|
||||||
|
fwrite(&fs_node_count, sizeof(int), 1, f);
|
||||||
|
fwrite(¤t_dir, sizeof(int), 1, f);
|
||||||
|
fwrite(fs_nodes, sizeof(fs_node_t), fs_node_count, f);
|
||||||
|
fclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the OS state from a file with our custom format.
|
||||||
|
int load_state(const char *filename) {
|
||||||
|
FILE *f = fopen(filename, "rb");
|
||||||
|
if (!f) {
|
||||||
|
perror("fopen for load_state");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
char header[8] = {0};
|
||||||
|
fread(header, sizeof(char), 7, f);
|
||||||
|
if (strcmp(header, "OSSTATE") != 0) {
|
||||||
|
fprintf(stderr, "Invalid state file header.\n");
|
||||||
|
fclose(f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int version;
|
||||||
|
fread(&version, sizeof(int), 1, f);
|
||||||
|
if (version != 1) {
|
||||||
|
fprintf(stderr, "Unsupported state version: %d\n", version);
|
||||||
|
fclose(f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fread(&fs_node_count, sizeof(int), 1, f);
|
||||||
|
fread(¤t_dir, sizeof(int), 1, f);
|
||||||
|
fread(fs_nodes, sizeof(fs_node_t), fs_node_count, f);
|
||||||
|
fclose(f);
|
||||||
|
os_state_initialized = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a child node by name in the directory at index 'dir_index'.
|
||||||
|
static int fs_find_child(int dir_index, const char *name) {
|
||||||
|
for (int i = 0; i < fs_nodes[dir_index].num_children; i++) {
|
||||||
|
int child_index = fs_nodes[dir_index].children[i];
|
||||||
|
if (child_index != -1 && strcmp(fs_nodes[child_index].name, name) == 0) {
|
||||||
|
return child_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new node (file or directory) under parent_index.
|
||||||
|
static int fs_add_node(int parent_index, const char *name, fs_type_t type) {
|
||||||
|
if (fs_node_count >= MAX_FS_NODES) {
|
||||||
|
printf("Error: Maximum file system nodes reached.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fs_find_child(parent_index, name) != -1) {
|
||||||
|
printf("Error: A file or directory with that name already exists.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int new_index = fs_node_count;
|
||||||
|
strncpy(fs_nodes[new_index].name, name, NAME_SIZE - 1);
|
||||||
|
fs_nodes[new_index].name[NAME_SIZE - 1] = '\0';
|
||||||
|
fs_nodes[new_index].type = type;
|
||||||
|
fs_nodes[new_index].parent = parent_index;
|
||||||
|
fs_nodes[new_index].num_children = 0;
|
||||||
|
for (int i = 0; i < MAX_CHILDREN; i++) {
|
||||||
|
fs_nodes[new_index].children[i] = -1;
|
||||||
|
}
|
||||||
|
if (type == FS_FILE) {
|
||||||
|
fs_nodes[new_index].content[0] = '\0';
|
||||||
|
}
|
||||||
|
if (fs_nodes[parent_index].num_children >= MAX_CHILDREN) {
|
||||||
|
printf("Error: Maximum children reached in this directory.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fs_nodes[parent_index].children[fs_nodes[parent_index].num_children++] = new_index;
|
||||||
|
fs_node_count++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a node by name from parent_index.
|
||||||
|
static int fs_remove_node(int parent_index, const char *name, fs_type_t expected_type) {
|
||||||
|
int child_index = fs_find_child(parent_index, name);
|
||||||
|
if (child_index == -1) {
|
||||||
|
printf("Error: No such file or directory: %s\n", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fs_nodes[child_index].type != expected_type) {
|
||||||
|
printf("Error: Type mismatch for %s.\n", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (expected_type == FS_DIR && fs_nodes[child_index].num_children > 0) {
|
||||||
|
printf("Error: Directory not empty: %s\n", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int pos = -1;
|
||||||
|
for (int i = 0; i < fs_nodes[parent_index].num_children; i++) {
|
||||||
|
if (fs_nodes[parent_index].children[i] == child_index) {
|
||||||
|
pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pos == -1) return -1;
|
||||||
|
for (int i = pos; i < fs_nodes[parent_index].num_children - 1; i++) {
|
||||||
|
fs_nodes[parent_index].children[i] = fs_nodes[parent_index].children[i+1];
|
||||||
|
}
|
||||||
|
fs_nodes[parent_index].num_children--;
|
||||||
|
printf("%s removed successfully.\n", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy a file from source to destination in the same directory.
|
||||||
|
static int fs_copy_file(int parent_index, const char *src_name, const char *dest_name) {
|
||||||
|
int src_index = fs_find_child(parent_index, src_name);
|
||||||
|
if (src_index == -1 || fs_nodes[src_index].type != FS_FILE) {
|
||||||
|
printf("Error: Source file does not exist: %s\n", src_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fs_find_child(parent_index, dest_name) != -1) {
|
||||||
|
printf("Error: Destination file already exists: %s\n", dest_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fs_add_node(parent_index, dest_name, FS_FILE) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int dest_index = fs_find_child(parent_index, dest_name);
|
||||||
|
strncpy(fs_nodes[dest_index].content, fs_nodes[src_index].content, FILE_CONTENT_SIZE - 1);
|
||||||
|
fs_nodes[dest_index].content[FILE_CONTENT_SIZE - 1] = '\0';
|
||||||
|
printf("Copied %s to %s successfully.\n", src_name, dest_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List contents of the directory at index 'dir_index'.
|
||||||
|
static void fs_list(int dir_index) {
|
||||||
|
printf("Contents of %s:\n", fs_nodes[dir_index].name);
|
||||||
|
for (int i = 0; i < fs_nodes[dir_index].num_children; i++) {
|
||||||
|
int child_index = fs_nodes[dir_index].children[i];
|
||||||
|
printf(" %s\t%s\n", fs_nodes[child_index].type == FS_DIR ? "DIR " : "FILE", fs_nodes[child_index].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively print the working directory.
|
||||||
|
static void fs_print_pwd(int dir_index) {
|
||||||
|
if (fs_nodes[dir_index].parent == -1) {
|
||||||
|
printf("/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs_print_pwd(fs_nodes[dir_index].parent);
|
||||||
|
if (strcmp(fs_nodes[dir_index].name, "/") != 0)
|
||||||
|
printf("%s/", fs_nodes[dir_index].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple text editor: user types lines ending with a single '.'.
|
||||||
|
static void fs_edit_file(int file_index) {
|
||||||
|
char buffer[INPUT_SIZE];
|
||||||
|
char new_content[FILE_CONTENT_SIZE] = "";
|
||||||
|
printf("Editing file: %s\n", fs_nodes[file_index].name);
|
||||||
|
printf("Enter text (end with a single '.' on a new line):\n");
|
||||||
|
while (1) {
|
||||||
|
if (!fgets(buffer, INPUT_SIZE, stdin)) break;
|
||||||
|
buffer[strcspn(buffer, "\n")] = '\0';
|
||||||
|
if (strcmp(buffer, ".") == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (strlen(new_content) + strlen(buffer) + 2 < FILE_CONTENT_SIZE) {
|
||||||
|
strcat(new_content, buffer);
|
||||||
|
strcat(new_content, "\n");
|
||||||
|
} else {
|
||||||
|
printf("Warning: File content limit reached.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strncpy(fs_nodes[file_index].content, new_content, FILE_CONTENT_SIZE - 1);
|
||||||
|
fs_nodes[file_index].content[FILE_CONTENT_SIZE - 1] = '\0';
|
||||||
|
printf("File saved.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- Command History (up to 16 commands) -----
|
||||||
|
#define MAX_HISTORY 16
|
||||||
|
static char history[MAX_HISTORY][INPUT_SIZE];
|
||||||
|
static int history_count = 0;
|
||||||
|
|
||||||
|
static void add_history(const char *cmd) {
|
||||||
|
if (strlen(cmd) == 0) return;
|
||||||
|
strncpy(history[history_count % MAX_HISTORY], cmd, INPUT_SIZE - 1);
|
||||||
|
history[history_count % MAX_HISTORY][INPUT_SIZE - 1] = '\0';
|
||||||
|
history_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_history(void) {
|
||||||
|
int start = history_count > MAX_HISTORY ? history_count - MAX_HISTORY : 0;
|
||||||
|
for (int i = start; i < history_count; i++) {
|
||||||
|
printf("%d: %s\n", i + 1, history[i % MAX_HISTORY]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- The Nested OS Shell -----
|
||||||
|
void run_os(void) {
|
||||||
|
char input[INPUT_SIZE];
|
||||||
|
|
||||||
|
// If a reload path was provided via command-line, load state now.
|
||||||
|
if (strlen(reload_path) > 0) {
|
||||||
|
if (load_state(reload_path) == 0) {
|
||||||
|
printf("OS state reloaded from %s\n", reload_path);
|
||||||
|
} else {
|
||||||
|
printf("Failed to load state from %s, initializing fresh state.\n", reload_path);
|
||||||
|
fs_init();
|
||||||
|
}
|
||||||
|
reload_path[0] = '\0'; // Clear reload path.
|
||||||
|
} else if (!os_state_initialized) {
|
||||||
|
fs_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Entering Nested OS Shell. Type 'exit' to return to VM.\n");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// Check for Control-L (form feed) input to clear screen.
|
||||||
|
printf("shell:");
|
||||||
|
fs_print_pwd(current_dir);
|
||||||
|
printf("> ");
|
||||||
|
if (!fgets(input, INPUT_SIZE, stdin))
|
||||||
|
break;
|
||||||
|
input[strcspn(input, "\n")] = '\0';
|
||||||
|
|
||||||
|
// If the user pressed Ctrl-L (ASCII 12), clear screen.
|
||||||
|
if (strcmp(input, "\f") == 0) {
|
||||||
|
printf("\033[H\033[J"); // ANSI escape code to clear screen.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate up-arrow by typing "up".
|
||||||
|
if (strcmp(input, "up") == 0 && history_count > 0) {
|
||||||
|
strncpy(input, history[(history_count - 1) % MAX_HISTORY], INPUT_SIZE - 1);
|
||||||
|
input[INPUT_SIZE - 1] = '\0';
|
||||||
|
printf("%s\n", input);
|
||||||
|
}
|
||||||
|
add_history(input);
|
||||||
|
|
||||||
|
// Tokenize input.
|
||||||
|
char *cmd = strtok(input, " ");
|
||||||
|
if (!cmd) continue;
|
||||||
|
|
||||||
|
if (strcmp(cmd, "exit") == 0) {
|
||||||
|
break;
|
||||||
|
} else if (strcmp(cmd, "help") == 0) {
|
||||||
|
printf("Available commands:\n");
|
||||||
|
printf(" help - Show this help message.\n");
|
||||||
|
printf(" exit - Exit the nested OS shell.\n");
|
||||||
|
printf(" ls - List directory contents.\n");
|
||||||
|
printf(" mkdir <name> - Create a new directory.\n");
|
||||||
|
printf(" rmdir <name> - Remove an empty directory.\n");
|
||||||
|
printf(" touch <name> - Create a new file.\n");
|
||||||
|
printf(" rm <name> - Remove a file.\n");
|
||||||
|
printf(" cp <src> <dest> - Copy a file.\n");
|
||||||
|
printf(" edit <name> - Edit a file using the built-in text editor.\n");
|
||||||
|
printf(" cat <name> - Display file contents.\n");
|
||||||
|
printf(" cd <name> - Change directory (.. for parent, / for root).\n");
|
||||||
|
printf(" pwd - Print working directory.\n");
|
||||||
|
printf(" history - Show command history.\n");
|
||||||
|
printf(" echo <text> - Print text.\n");
|
||||||
|
printf(" save [filename] - Save OS state (default: states/save.osstate).\n");
|
||||||
|
} else if (strcmp(cmd, "ls") == 0) {
|
||||||
|
fs_list(current_dir);
|
||||||
|
} else if (strcmp(cmd, "mkdir") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: mkdir <directory_name>\n");
|
||||||
|
} else {
|
||||||
|
fs_add_node(current_dir, name, FS_DIR);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "rmdir") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: rmdir <directory_name>\n");
|
||||||
|
} else {
|
||||||
|
fs_remove_node(current_dir, name, FS_DIR);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "touch") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: touch <file_name>\n");
|
||||||
|
} else {
|
||||||
|
fs_add_node(current_dir, name, FS_FILE);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "rm") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: rm <file_name>\n");
|
||||||
|
} else {
|
||||||
|
fs_remove_node(current_dir, name, FS_FILE);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "cp") == 0) {
|
||||||
|
char *src = strtok(NULL, " ");
|
||||||
|
char *dest = strtok(NULL, " ");
|
||||||
|
if (!src || !dest) {
|
||||||
|
printf("Usage: cp <source_file> <destination_file>\n");
|
||||||
|
} else {
|
||||||
|
fs_copy_file(current_dir, src, dest);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "edit") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: edit <file_name>\n");
|
||||||
|
} else {
|
||||||
|
int file_index = fs_find_child(current_dir, name);
|
||||||
|
if (file_index == -1) {
|
||||||
|
if (fs_add_node(current_dir, name, FS_FILE) != 0)
|
||||||
|
continue;
|
||||||
|
file_index = fs_find_child(current_dir, name);
|
||||||
|
}
|
||||||
|
fs_edit_file(file_index);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "cat") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: cat <file_name>\n");
|
||||||
|
} else {
|
||||||
|
int child_index = fs_find_child(current_dir, name);
|
||||||
|
if (child_index == -1 || fs_nodes[child_index].type != FS_FILE) {
|
||||||
|
printf("Error: No such file: %s\n", name);
|
||||||
|
} else {
|
||||||
|
printf("Contents of %s:\n", name);
|
||||||
|
if (strlen(fs_nodes[child_index].content) > 0)
|
||||||
|
printf("%s", fs_nodes[child_index].content);
|
||||||
|
else
|
||||||
|
printf("[Empty file]\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "cd") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: cd <directory_name>\n");
|
||||||
|
} else if (strcmp(name, "..") == 0) {
|
||||||
|
if (fs_nodes[current_dir].parent != -1) {
|
||||||
|
current_dir = fs_nodes[current_dir].parent;
|
||||||
|
}
|
||||||
|
} else if (strcmp(name, "/") == 0) {
|
||||||
|
current_dir = 0;
|
||||||
|
} else {
|
||||||
|
int child_index = fs_find_child(current_dir, name);
|
||||||
|
if (child_index == -1 || fs_nodes[child_index].type != FS_DIR) {
|
||||||
|
printf("Error: No such directory: %s\n", name);
|
||||||
|
} else {
|
||||||
|
current_dir = child_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "pwd") == 0) {
|
||||||
|
fs_print_pwd(current_dir);
|
||||||
|
printf("\n");
|
||||||
|
} else if (strcmp(cmd, "history") == 0) {
|
||||||
|
print_history();
|
||||||
|
} else if (strcmp(cmd, "echo") == 0) {
|
||||||
|
char *text = strtok(NULL, "\n");
|
||||||
|
if (text) {
|
||||||
|
printf("%s\n", text);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "save") == 0) {
|
||||||
|
char *fname = strtok(NULL, " ");
|
||||||
|
const char *filename = fname ? fname : "states/save.osstate";
|
||||||
|
if (save_state(filename) == 0) {
|
||||||
|
printf("State saved to %s\n", filename);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Unknown command. Type 'help' for available commands.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Exiting Nested OS Shell.\n");
|
||||||
|
}
|
||||||
|
|
||||||
247
src/vm.c
Normal file
247
src/vm.c
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "vm.h"
|
||||||
|
#include "opcodes.h"
|
||||||
|
#include "os.h"
|
||||||
|
|
||||||
|
// Initialize the virtual machine by resetting registers, stack pointer,
|
||||||
|
// program counter, and clearing program memory.
|
||||||
|
void init_vm(VM *vm) {
|
||||||
|
for (int i = 0; i < NUM_REGISTERS; i++) {
|
||||||
|
vm->registers[i] = 0;
|
||||||
|
}
|
||||||
|
vm->sp = 0;
|
||||||
|
vm->pc = 0;
|
||||||
|
vm->program_size = 0;
|
||||||
|
for (int i = 0; i < PROGRAM_SIZE; i++) {
|
||||||
|
vm->program[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the given program (array of ints) into the VM's memory.
|
||||||
|
// Returns 0 on success, or -1 if the program is too large.
|
||||||
|
int load_program(VM *vm, const int *program, size_t size) {
|
||||||
|
if (size > PROGRAM_SIZE) {
|
||||||
|
fprintf(stderr, "Error: Program size exceeds memory limit.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
vm->program[i] = program[i];
|
||||||
|
}
|
||||||
|
vm->program_size = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the loaded program.
|
||||||
|
void run_vm(VM *vm) {
|
||||||
|
while (vm->pc < vm->program_size) {
|
||||||
|
int opcode = vm->program[vm->pc];
|
||||||
|
switch (opcode) {
|
||||||
|
case OP_HALT: {
|
||||||
|
// Halt execution.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case OP_PUSH: {
|
||||||
|
if (vm->pc + 1 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_PUSH missing operand at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int value = vm->program[vm->pc + 1];
|
||||||
|
if (vm->sp >= STACK_SIZE) {
|
||||||
|
fprintf(stderr, "Error: Stack overflow in OP_PUSH.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->stack[vm->sp++] = value;
|
||||||
|
vm->pc += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_POP: {
|
||||||
|
if (vm->pc + 1 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_POP missing operand at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg = vm->program[vm->pc + 1];
|
||||||
|
if (reg < 0 || reg >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register %d in OP_POP.\n", reg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vm->sp <= 0) {
|
||||||
|
fprintf(stderr, "Error: Stack underflow in OP_POP.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg] = vm->stack[--vm->sp];
|
||||||
|
vm->pc += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_LOAD: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_LOAD missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg = vm->program[vm->pc + 1];
|
||||||
|
int value = vm->program[vm->pc + 2];
|
||||||
|
if (reg < 0 || reg >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register %d in OP_LOAD.\n", reg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg] = value;
|
||||||
|
vm->pc += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_ADD: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_ADD missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg_dest = vm->program[vm->pc + 1];
|
||||||
|
int reg_src = vm->program[vm->pc + 2];
|
||||||
|
if (reg_dest < 0 || reg_dest >= NUM_REGISTERS ||
|
||||||
|
reg_src < 0 || reg_src >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_ADD.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg_dest] += vm->registers[reg_src];
|
||||||
|
vm->pc += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_SUB: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_SUB missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg_dest = vm->program[vm->pc + 1];
|
||||||
|
int reg_src = vm->program[vm->pc + 2];
|
||||||
|
if (reg_dest < 0 || reg_dest >= NUM_REGISTERS ||
|
||||||
|
reg_src < 0 || reg_src >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_SUB.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg_dest] -= vm->registers[reg_src];
|
||||||
|
vm->pc += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_MUL: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_MUL missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg_dest = vm->program[vm->pc + 1];
|
||||||
|
int reg_src = vm->program[vm->pc + 2];
|
||||||
|
if (reg_dest < 0 || reg_dest >= NUM_REGISTERS ||
|
||||||
|
reg_src < 0 || reg_src >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_MUL.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg_dest] *= vm->registers[reg_src];
|
||||||
|
vm->pc += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_DIV: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_DIV missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg_dest = vm->program[vm->pc + 1];
|
||||||
|
int reg_src = vm->program[vm->pc + 2];
|
||||||
|
if (reg_dest < 0 || reg_dest >= NUM_REGISTERS ||
|
||||||
|
reg_src < 0 || reg_src >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_DIV.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vm->registers[reg_src] == 0) {
|
||||||
|
fprintf(stderr, "Error: Division by zero in OP_DIV.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg_dest] /= vm->registers[reg_src];
|
||||||
|
vm->pc += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_PRINT: {
|
||||||
|
if (vm->pc + 1 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_PRINT missing operand at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg = vm->program[vm->pc + 1];
|
||||||
|
if (reg < 0 || reg >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_PRINT.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("Register %d: %d\n", reg, vm->registers[reg]);
|
||||||
|
vm->pc += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_JMP: {
|
||||||
|
if (vm->pc + 1 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_JMP missing operand at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int address = vm->program[vm->pc + 1];
|
||||||
|
if (address < 0 || (size_t)address >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: Invalid jump address %d in OP_JMP.\n", address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->pc = address;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_JMPZ: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_JMPZ missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg = vm->program[vm->pc + 1];
|
||||||
|
int address = vm->program[vm->pc + 2];
|
||||||
|
if (reg < 0 || reg >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_JMPZ.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (address < 0 || (size_t)address >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: Invalid jump address %d in OP_JMPZ.\n", address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vm->registers[reg] == 0) {
|
||||||
|
vm->pc = address;
|
||||||
|
} else {
|
||||||
|
vm->pc += 3;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_CALL: {
|
||||||
|
if (vm->pc + 1 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_CALL missing operand at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int address = vm->program[vm->pc + 1];
|
||||||
|
if (address < 0 || (size_t)address >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: Invalid call address %d in OP_CALL.\n", address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vm->sp >= STACK_SIZE) {
|
||||||
|
fprintf(stderr, "Error: Stack overflow in OP_CALL.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Push return address.
|
||||||
|
vm->stack[vm->sp++] = vm->pc + 2;
|
||||||
|
vm->pc = address;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_RET: {
|
||||||
|
if (vm->sp <= 0) {
|
||||||
|
fprintf(stderr, "Error: Stack underflow in OP_RET.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->pc = vm->stack[--vm->sp];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_CALL_OS: {
|
||||||
|
// Enter the nested OS shell.
|
||||||
|
run_os();
|
||||||
|
vm->pc += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
fprintf(stderr, "Error: Unknown opcode %d at PC %zu.\n", opcode, vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
615
src/vm.c.bak
Normal file
615
src/vm.c.bak
Normal file
@@ -0,0 +1,615 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "os.h"
|
||||||
|
#include "vm.h"
|
||||||
|
|
||||||
|
#define INPUT_SIZE 128
|
||||||
|
|
||||||
|
// New macro for file content size.
|
||||||
|
#define FILE_CONTENT_SIZE 1024
|
||||||
|
|
||||||
|
// ----- In-Memory File System Definitions -----
|
||||||
|
#define MAX_FS_NODES 64 // Maximum number of file system nodes.
|
||||||
|
#define MAX_CHILDREN 16 // Maximum children per directory.
|
||||||
|
#define NAME_SIZE 32 // Maximum length for file/directory names.
|
||||||
|
|
||||||
|
typedef enum { FS_DIR, FS_FILE } fs_type_t;
|
||||||
|
|
||||||
|
// Structure representing a file or directory.
|
||||||
|
// Modified to include file content for FS_FILE nodes.
|
||||||
|
typedef struct fs_node {
|
||||||
|
char name[NAME_SIZE]; // Name of the node.
|
||||||
|
fs_type_t type; // FS_DIR or FS_FILE.
|
||||||
|
int parent; // Index of parent node (-1 for root).
|
||||||
|
int children[MAX_CHILDREN]; // Indices of children (if directory).
|
||||||
|
int num_children; // Count of children.
|
||||||
|
char content[FILE_CONTENT_SIZE]; // Content for files (empty for directories).
|
||||||
|
} fs_node_t;
|
||||||
|
|
||||||
|
static fs_node_t fs_nodes[MAX_FS_NODES];
|
||||||
|
static int fs_node_count = 0;
|
||||||
|
static int current_dir = 0; // Index of current directory in fs_nodes.
|
||||||
|
|
||||||
|
// Initialize the file system with a root directory.
|
||||||
|
static void fs_init() {
|
||||||
|
fs_node_count = 0;
|
||||||
|
current_dir = 0;
|
||||||
|
// Create root directory.
|
||||||
|
strncpy(fs_nodes[0].name, "/", NAME_SIZE);
|
||||||
|
fs_nodes[0].type = FS_DIR;
|
||||||
|
fs_nodes[0].parent = -1;
|
||||||
|
fs_nodes[0].num_children = 0;
|
||||||
|
fs_nodes[0].content[0] = '\0'; // Directories have no content.
|
||||||
|
for (int i = 0; i < MAX_CHILDREN; i++) {
|
||||||
|
fs_nodes[0].children[i] = -1;
|
||||||
|
}
|
||||||
|
fs_node_count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a child node by name within the directory at index 'dir_index'.
|
||||||
|
// Returns the index of the child if found, or -1 if not found.
|
||||||
|
static int fs_find_child(int dir_index, const char *name) {
|
||||||
|
for (int i = 0; i < fs_nodes[dir_index].num_children; i++) {
|
||||||
|
int child_index = fs_nodes[dir_index].children[i];
|
||||||
|
if (child_index != -1 && strcmp(fs_nodes[child_index].name, name) == 0) {
|
||||||
|
return child_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new node (file or directory) to the file system under the given parent.
|
||||||
|
// Returns 0 on success, -1 on failure.
|
||||||
|
static int fs_add_node(int parent_index, const char *name, fs_type_t type) {
|
||||||
|
if (fs_node_count >= MAX_FS_NODES) {
|
||||||
|
printf("Error: Maximum file system nodes reached.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Check if a node with the same name exists.
|
||||||
|
if (fs_find_child(parent_index, name) != -1) {
|
||||||
|
printf("Error: A file or directory with that name already exists.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Create new node.
|
||||||
|
int new_index = fs_node_count;
|
||||||
|
strncpy(fs_nodes[new_index].name, name, NAME_SIZE - 1);
|
||||||
|
fs_nodes[new_index].name[NAME_SIZE - 1] = '\0';
|
||||||
|
fs_nodes[new_index].type = type;
|
||||||
|
fs_nodes[new_index].parent = parent_index;
|
||||||
|
fs_nodes[new_index].num_children = 0;
|
||||||
|
for (int i = 0; i < MAX_CHILDREN; i++) {
|
||||||
|
fs_nodes[new_index].children[i] = -1;
|
||||||
|
}
|
||||||
|
// Initialize content for files.
|
||||||
|
if (type == FS_FILE) {
|
||||||
|
fs_nodes[new_index].content[0] = '\0'; // Empty content initially.
|
||||||
|
}
|
||||||
|
// Add new node to parent's children.
|
||||||
|
if (fs_nodes[parent_index].num_children >= MAX_CHILDREN) {
|
||||||
|
printf("Error: Maximum children reached in this directory.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fs_nodes[parent_index].children[fs_nodes[parent_index].num_children++] = new_index;
|
||||||
|
fs_node_count++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a node with the given name from the directory at parent_index.
|
||||||
|
// For directories, only allow removal if empty.
|
||||||
|
// Returns 0 on success, -1 on failure.
|
||||||
|
static int fs_remove_node(int parent_index, const char *name, fs_type_t expected_type) {
|
||||||
|
int child_index = fs_find_child(parent_index, name);
|
||||||
|
if (child_index == -1) {
|
||||||
|
printf("Error: No such file or directory: %s\n", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fs_nodes[child_index].type != expected_type) {
|
||||||
|
printf("Error: Type mismatch for %s.\n", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (expected_type == FS_DIR && fs_nodes[child_index].num_children > 0) {
|
||||||
|
printf("Error: Directory not empty: %s\n", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Remove child by shifting remaining children.
|
||||||
|
int pos = -1;
|
||||||
|
for (int i = 0; i < fs_nodes[parent_index].num_children; i++) {
|
||||||
|
if (fs_nodes[parent_index].children[i] == child_index) {
|
||||||
|
pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pos == -1) return -1; // Should not happen.
|
||||||
|
for (int i = pos; i < fs_nodes[parent_index].num_children - 1; i++) {
|
||||||
|
fs_nodes[parent_index].children[i] = fs_nodes[parent_index].children[i+1];
|
||||||
|
}
|
||||||
|
fs_nodes[parent_index].num_children--;
|
||||||
|
printf("%s removed successfully.\n", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy a file from source node to a new file with destination name in the same directory.
|
||||||
|
// Returns 0 on success, -1 on failure.
|
||||||
|
static int fs_copy_file(int parent_index, const char *src_name, const char *dest_name) {
|
||||||
|
int src_index = fs_find_child(parent_index, src_name);
|
||||||
|
if (src_index == -1 || fs_nodes[src_index].type != FS_FILE) {
|
||||||
|
printf("Error: Source file does not exist: %s\n", src_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Check if destination already exists.
|
||||||
|
if (fs_find_child(parent_index, dest_name) != -1) {
|
||||||
|
printf("Error: Destination file already exists: %s\n", dest_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Add new file node.
|
||||||
|
if (fs_add_node(parent_index, dest_name, FS_FILE) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int dest_index = fs_find_child(parent_index, dest_name);
|
||||||
|
// Copy content from source to destination.
|
||||||
|
strncpy(fs_nodes[dest_index].content, fs_nodes[src_index].content, FILE_CONTENT_SIZE - 1);
|
||||||
|
fs_nodes[dest_index].content[FILE_CONTENT_SIZE - 1] = '\0';
|
||||||
|
printf("Copied %s to %s successfully.\n", src_name, dest_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List the contents of the current directory.
|
||||||
|
static void fs_list(int dir_index) {
|
||||||
|
printf("Contents of %s:\n", fs_nodes[dir_index].name);
|
||||||
|
for (int i = 0; i < fs_nodes[dir_index].num_children; i++) {
|
||||||
|
int child_index = fs_nodes[dir_index].children[i];
|
||||||
|
printf(" %s\t%s\n", fs_nodes[child_index].type == FS_DIR ? "DIR " : "FILE", fs_nodes[child_index].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively build the path from the current directory.
|
||||||
|
static void fs_print_pwd(int dir_index) {
|
||||||
|
if (fs_nodes[dir_index].parent == -1) {
|
||||||
|
printf("/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs_print_pwd(fs_nodes[dir_index].parent);
|
||||||
|
if (strcmp(fs_nodes[dir_index].name, "/") != 0)
|
||||||
|
printf("%s/", fs_nodes[dir_index].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple text editor for editing file content.
|
||||||
|
// Prompts the user to input multiple lines, ending with a single '.' on a new line.
|
||||||
|
static void fs_edit_file(int file_index) {
|
||||||
|
char buffer[INPUT_SIZE];
|
||||||
|
char new_content[FILE_CONTENT_SIZE] = "";
|
||||||
|
printf("Editing file: %s\n", fs_nodes[file_index].name);
|
||||||
|
printf("Enter text (end with a single '.' on a new line):\n");
|
||||||
|
while (1) {
|
||||||
|
if (!fgets(buffer, INPUT_SIZE, stdin)) break;
|
||||||
|
buffer[strcspn(buffer, "\n")] = '\0';
|
||||||
|
if (strcmp(buffer, ".") == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (strlen(new_content) + strlen(buffer) + 2 < FILE_CONTENT_SIZE) {
|
||||||
|
strcat(new_content, buffer);
|
||||||
|
strcat(new_content, "\n");
|
||||||
|
} else {
|
||||||
|
printf("Warning: File content limit reached.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strncpy(fs_nodes[file_index].content, new_content, FILE_CONTENT_SIZE - 1);
|
||||||
|
fs_nodes[file_index].content[FILE_CONTENT_SIZE - 1] = '\0';
|
||||||
|
printf("File saved.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- Command History (up to 16 commands) -----
|
||||||
|
#define MAX_HISTORY 16
|
||||||
|
static char history[MAX_HISTORY][INPUT_SIZE];
|
||||||
|
static int history_count = 0;
|
||||||
|
|
||||||
|
// Add a command to the history.
|
||||||
|
static void add_history(const char *cmd) {
|
||||||
|
if (strlen(cmd) == 0) return;
|
||||||
|
strncpy(history[history_count % MAX_HISTORY], cmd, INPUT_SIZE - 1);
|
||||||
|
history[history_count % MAX_HISTORY][INPUT_SIZE - 1] = '\0';
|
||||||
|
history_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the command history.
|
||||||
|
static void print_history() {
|
||||||
|
int start = history_count > MAX_HISTORY ? history_count - MAX_HISTORY : 0;
|
||||||
|
for (int i = start; i < history_count; i++) {
|
||||||
|
printf("%d: %s\n", i + 1, history[i % MAX_HISTORY]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the VM: zero registers, reset stack pointer, PC, and clear program memory.
|
||||||
|
void init_vm(VM *vm) {
|
||||||
|
for (int i = 0; i < NUM_REGISTERS; i++) {
|
||||||
|
vm->registers[i] = 0;
|
||||||
|
}
|
||||||
|
vm->sp = 0;
|
||||||
|
vm->pc = 0;
|
||||||
|
vm->program_size = 0;
|
||||||
|
for (int i = 0; i < PROGRAM_SIZE; i++) {
|
||||||
|
vm->program[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the given program (array of ints) into the VM's memory.
|
||||||
|
int load_program(VM *vm, const int *program, size_t size) {
|
||||||
|
if (size > PROGRAM_SIZE) {
|
||||||
|
fprintf(stderr, "Error: Program size exceeds memory limit.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
vm->program[i] = program[i];
|
||||||
|
}
|
||||||
|
vm->program_size = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the loaded program.
|
||||||
|
void run_vm(VM *vm) {
|
||||||
|
while (vm->pc < vm->program_size) {
|
||||||
|
int opcode = vm->program[vm->pc];
|
||||||
|
switch (opcode) {
|
||||||
|
case OP_HALT: {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case OP_PUSH: {
|
||||||
|
if (vm->pc + 1 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_PUSH missing operand at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int value = vm->program[vm->pc + 1];
|
||||||
|
if (vm->sp >= STACK_SIZE) {
|
||||||
|
fprintf(stderr, "Error: Stack overflow in OP_PUSH.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->stack[vm->sp++] = value;
|
||||||
|
vm->pc += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_POP: {
|
||||||
|
if (vm->pc + 1 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_POP missing operand at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg = vm->program[vm->pc + 1];
|
||||||
|
if (reg < 0 || reg >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register %d in OP_POP.\n", reg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vm->sp <= 0) {
|
||||||
|
fprintf(stderr, "Error: Stack underflow in OP_POP.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg] = vm->stack[--vm->sp];
|
||||||
|
vm->pc += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_LOAD: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_LOAD missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg = vm->program[vm->pc + 1];
|
||||||
|
int value = vm->program[vm->pc + 2];
|
||||||
|
if (reg < 0 || reg >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register %d in OP_LOAD.\n", reg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg] = value;
|
||||||
|
vm->pc += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_ADD: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_ADD missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg_dest = vm->program[vm->pc + 1];
|
||||||
|
int reg_src = vm->program[vm->pc + 2];
|
||||||
|
if (reg_dest < 0 || reg_dest >= NUM_REGISTERS ||
|
||||||
|
reg_src < 0 || reg_src >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_ADD.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg_dest] += vm->registers[reg_src];
|
||||||
|
vm->pc += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_SUB: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_SUB missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg_dest = vm->program[vm->pc + 1];
|
||||||
|
int reg_src = vm->program[vm->pc + 2];
|
||||||
|
if (reg_dest < 0 || reg_dest >= NUM_REGISTERS ||
|
||||||
|
reg_src < 0 || reg_src >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_SUB.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg_dest] -= vm->registers[reg_src];
|
||||||
|
vm->pc += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_MUL: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_MUL missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg_dest = vm->program[vm->pc + 1];
|
||||||
|
int reg_src = vm->program[vm->pc + 2];
|
||||||
|
if (reg_dest < 0 || reg_dest >= NUM_REGISTERS ||
|
||||||
|
reg_src < 0 || reg_src >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_MUL.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg_dest] *= vm->registers[reg_src];
|
||||||
|
vm->pc += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_DIV: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_DIV missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg_dest = vm->program[vm->pc + 1];
|
||||||
|
int reg_src = vm->program[vm->pc + 2];
|
||||||
|
if (reg_dest < 0 || reg_dest >= NUM_REGISTERS ||
|
||||||
|
reg_src < 0 || reg_src >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_DIV.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vm->registers[reg_src] == 0) {
|
||||||
|
fprintf(stderr, "Error: Division by zero in OP_DIV.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->registers[reg_dest] /= vm->registers[reg_src];
|
||||||
|
vm->pc += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_PRINT: {
|
||||||
|
if (vm->pc + 1 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_PRINT missing operand at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg = vm->program[vm->pc + 1];
|
||||||
|
if (reg < 0 || reg >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_PRINT.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("Register %d: %d\n", reg, vm->registers[reg]);
|
||||||
|
vm->pc += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_JMP: {
|
||||||
|
if (vm->pc + 1 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_JMP missing operand at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int address = vm->program[vm->pc + 1];
|
||||||
|
if (address < 0 || (size_t)address >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: Invalid jump address %d in OP_JMP.\n", address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->pc = address;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_JMPZ: {
|
||||||
|
if (vm->pc + 2 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_JMPZ missing operands at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int reg = vm->program[vm->pc + 1];
|
||||||
|
int address = vm->program[vm->pc + 2];
|
||||||
|
if (reg < 0 || reg >= NUM_REGISTERS) {
|
||||||
|
fprintf(stderr, "Error: Invalid register in OP_JMPZ.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (address < 0 || (size_t)address >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: Invalid jump address %d in OP_JMPZ.\n", address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vm->registers[reg] == 0) {
|
||||||
|
vm->pc = address;
|
||||||
|
} else {
|
||||||
|
vm->pc += 3;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_CALL: {
|
||||||
|
if (vm->pc + 1 >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: OP_CALL missing operand at PC %zu.\n", vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int address = vm->program[vm->pc + 1];
|
||||||
|
if (address < 0 || (size_t)address >= vm->program_size) {
|
||||||
|
fprintf(stderr, "Error: Invalid call address %d in OP_CALL.\n", address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vm->sp >= STACK_SIZE) {
|
||||||
|
fprintf(stderr, "Error: Stack overflow in OP_CALL.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->stack[vm->sp++] = vm->pc + 2;
|
||||||
|
vm->pc = address;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_RET: {
|
||||||
|
if (vm->sp <= 0) {
|
||||||
|
fprintf(stderr, "Error: Stack underflow in OP_RET.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vm->pc = vm->stack[--vm->sp];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_CALL_OS: {
|
||||||
|
// Only call the OS shell as defined in os.c (no duplicate definition here)
|
||||||
|
run_os();
|
||||||
|
vm->pc += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
fprintf(stderr, "Error: Unknown opcode %d at PC %zu.\n", opcode, vm->pc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- The Nested OS Shell -----
|
||||||
|
void run_os() {
|
||||||
|
char input[INPUT_SIZE];
|
||||||
|
printf("Entering Nested OS Shell. Type 'exit' to return to VM.\n");
|
||||||
|
|
||||||
|
// Initialize file system on first entry.
|
||||||
|
fs_init();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// Update prompt with current directory.
|
||||||
|
printf("shell:");
|
||||||
|
fs_print_pwd(current_dir);
|
||||||
|
printf("> ");
|
||||||
|
if (!fgets(input, INPUT_SIZE, stdin)) {
|
||||||
|
break; // Exit on input error.
|
||||||
|
}
|
||||||
|
input[strcspn(input, "\n")] = '\0';
|
||||||
|
|
||||||
|
// Check for up-arrow simulation: if user types "up", recall last command.
|
||||||
|
if (strcmp(input, "up") == 0 && history_count > 0) {
|
||||||
|
strncpy(input, history[(history_count - 1) % MAX_HISTORY], INPUT_SIZE - 1);
|
||||||
|
input[INPUT_SIZE - 1] = '\0';
|
||||||
|
printf("%s\n", input);
|
||||||
|
}
|
||||||
|
add_history(input);
|
||||||
|
|
||||||
|
// Tokenize input.
|
||||||
|
char *cmd = strtok(input, " ");
|
||||||
|
if (!cmd) continue;
|
||||||
|
|
||||||
|
// Process commands:
|
||||||
|
if (strcmp(cmd, "exit") == 0) {
|
||||||
|
break;
|
||||||
|
} else if (strcmp(cmd, "help") == 0) {
|
||||||
|
printf("Available commands:\n");
|
||||||
|
printf(" help - Show this help message.\n");
|
||||||
|
printf(" exit - Exit the nested OS shell.\n");
|
||||||
|
printf(" ls - List directory contents.\n");
|
||||||
|
printf(" mkdir <name> - Create a new directory.\n");
|
||||||
|
printf(" rmdir <name> - Remove an empty directory.\n");
|
||||||
|
printf(" touch <name> - Create a new file.\n");
|
||||||
|
printf(" rm <name> - Remove a file.\n");
|
||||||
|
printf(" cp <src> <dest> - Copy a file.\n");
|
||||||
|
printf(" edit <name> - Edit a file using the built-in text editor.\n");
|
||||||
|
printf(" cat <name> - Display file contents.\n");
|
||||||
|
printf(" cd <name> - Change directory (.. for parent, / for root).\n");
|
||||||
|
printf(" pwd - Print working directory.\n");
|
||||||
|
printf(" history - Show command history.\n");
|
||||||
|
printf(" echo <text> - Print text.\n");
|
||||||
|
} else if (strcmp(cmd, "ls") == 0) {
|
||||||
|
fs_list(current_dir);
|
||||||
|
} else if (strcmp(cmd, "mkdir") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: mkdir <directory_name>\n");
|
||||||
|
} else {
|
||||||
|
fs_add_node(current_dir, name, FS_DIR);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "rmdir") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: rmdir <directory_name>\n");
|
||||||
|
} else {
|
||||||
|
fs_remove_node(current_dir, name, FS_DIR);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "touch") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: touch <file_name>\n");
|
||||||
|
} else {
|
||||||
|
fs_add_node(current_dir, name, FS_FILE);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "rm") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: rm <file_name>\n");
|
||||||
|
} else {
|
||||||
|
fs_remove_node(current_dir, name, FS_FILE);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "cp") == 0) {
|
||||||
|
char *src = strtok(NULL, " ");
|
||||||
|
char *dest = strtok(NULL, " ");
|
||||||
|
if (!src || !dest) {
|
||||||
|
printf("Usage: cp <source_file> <destination_file>\n");
|
||||||
|
} else {
|
||||||
|
fs_copy_file(current_dir, src, dest);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "edit") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: edit <file_name>\n");
|
||||||
|
} else {
|
||||||
|
int file_index = fs_find_child(current_dir, name);
|
||||||
|
if (file_index == -1) {
|
||||||
|
// If file doesn't exist, create it.
|
||||||
|
if (fs_add_node(current_dir, name, FS_FILE) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
file_index = fs_find_child(current_dir, name);
|
||||||
|
}
|
||||||
|
fs_edit_file(file_index);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "cat") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: cat <file_name>\n");
|
||||||
|
} else {
|
||||||
|
int child_index = fs_find_child(current_dir, name);
|
||||||
|
if (child_index == -1 || fs_nodes[child_index].type != FS_FILE) {
|
||||||
|
printf("Error: No such file: %s\n", name);
|
||||||
|
} else {
|
||||||
|
printf("Contents of %s:\n", name);
|
||||||
|
if (strlen(fs_nodes[child_index].content) > 0)
|
||||||
|
printf("%s", fs_nodes[child_index].content);
|
||||||
|
else
|
||||||
|
printf("[Empty file]\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "cd") == 0) {
|
||||||
|
char *name = strtok(NULL, " ");
|
||||||
|
if (!name) {
|
||||||
|
printf("Usage: cd <directory_name>\n");
|
||||||
|
} else if (strcmp(name, "..") == 0) {
|
||||||
|
if (fs_nodes[current_dir].parent != -1) {
|
||||||
|
current_dir = fs_nodes[current_dir].parent;
|
||||||
|
}
|
||||||
|
} else if (strcmp(name, "/") == 0) {
|
||||||
|
current_dir = 0;
|
||||||
|
} else {
|
||||||
|
int child_index = fs_find_child(current_dir, name);
|
||||||
|
if (child_index == -1 || fs_nodes[child_index].type != FS_DIR) {
|
||||||
|
printf("Error: No such directory: %s\n", name);
|
||||||
|
} else {
|
||||||
|
current_dir = child_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd, "pwd") == 0) {
|
||||||
|
fs_print_pwd(current_dir);
|
||||||
|
printf("\n");
|
||||||
|
} else if (strcmp(cmd, "history") == 0) {
|
||||||
|
print_history();
|
||||||
|
} else if (strcmp(cmd, "echo") == 0) {
|
||||||
|
char *text = strtok(NULL, "\n");
|
||||||
|
if (text) {
|
||||||
|
printf("%s\n", text);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Unknown command. Type 'help' for available commands.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Exiting Nested OS Shell.\n");
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user