Files
BarotraumaLuaProjects/atlas_os/CMC.lua
2026-06-14 02:27:37 +03:00

302 lines
7.7 KiB
Lua

-- CMC.lua — Command Controller (AtlasOS Shell/CPU v2.0)
-- in[1] = cmd_rx ← IOC.out[4] (IN|command)
-- in[2] = mem_rx ← MMC.out[1] (FS response)
-- out[1] = disp_tx → IOC.in[2] (OUT|text / CLR / COL|R,G,B)
-- out[4] = mem_tx → MMC.in[1] (FS request)
local ST_IDLE = 0
local ST_WAIT_FS = 1
local state = ST_IDLE
local uid, gid, cwd, curUser = 0, 0, "/", "root"
local lastRespSeq = 0
local outQueue = {}
local pendingCmd = nil
local function parseMsg(msg)
local p = msg:find("|")
return p and msg:sub(1, p - 1) or msg, p and msg:sub(p + 1) or nil
end
local function resolvePath(path)
if not path or path == "" then return cwd end
if path:sub(1, 1) == "/" then return path end
if cwd:sub(-1) == "/" then return cwd .. path end
return cwd .. "/" .. path
end
local function fsReq(op, arg)
out[4] = op .. (arg and ("|" .. arg) or "")
end
-- queue items: op, value (op = "t" text, "c" color, "x" clear)
local function qText(v)
outQueue[#outQueue + 1] = { "t", tostring(v) }
end
local function qErr(v)
outQueue[#outQueue + 1] = { "c", "255,40,40" }
outQueue[#outQueue + 1] = { "t", tostring(v) }
outQueue[#outQueue + 1] = { "c", "0,255,0" }
end
local function qClear()
outQueue[#outQueue + 1] = { "x" }
end
local function qColor(r, g, b)
outQueue[#outQueue + 1] = { "c", r .. "," .. g .. "," .. b }
end
local function queueLines(lines)
for i = 1, #lines do
qText(lines[i])
end
end
local cmds = {}
local function reg(name, fn, desc)
cmds[name] = { fn = fn, desc = desc }
end
reg("help", function(a)
local lines = { curUser .. " " .. cwd .. "> help" }
lines[#lines + 1] = "AtlasOS v2.0:"
for n, e in pairs(cmds) do
lines[#lines + 1] = " " .. n .. " " .. e.desc
end
queueLines(lines)
end, "This help")
reg("echo", function(a)
if #a == 0 then error("echo: missing text") end
local out = ""
for i = 1, #a do
if #out > 0 then out = out .. " " end
out = out .. tostring(a[i])
end
queueLines({ curUser .. " " .. cwd .. "> echo " .. out, out })
end, "Echo text")
reg("clear", function(a)
qClear()
end, "Clear screen")
reg("color", function(a)
if #a < 3 then error("color: need R G B") end
local r, g, b = tonumber(a[1]), tonumber(a[2]), tonumber(a[3])
if not (r and g and b) then error("color: invalid values") end
r = math.floor(math.max(0, math.min(255, r)))
g = math.floor(math.max(0, math.min(255, g)))
b = math.floor(math.max(0, math.min(255, b)))
qColor(r, g, b)
queueLines({ curUser .. " " .. cwd .. "> color",
"Color set to " .. r .. "," .. g .. "," .. b })
end, "Set color R G B")
reg("status", function(a)
pendingCmd = { name = "status",
prompt = curUser .. " " .. cwd .. "> status" }
state = ST_WAIT_FS
fsReq("STATFS", "")
end, "System status")
reg("ls", function(a)
local path = cwd
local opts = {}
for i = 1, #a do
if a[i]:sub(1, 1) == "-" then
opts[a[i]] = true
else
path = resolvePath(a[i])
end
end
local cmdStr = "ls"
if #a > 0 then cmdStr = "ls " .. table.concat(a, " ") end
pendingCmd = { name = "ls", opts = opts, path = path,
prompt = curUser .. " " .. cwd .. "> " .. cmdStr }
state = ST_WAIT_FS
fsReq("LS", path)
end, "List directory [path]")
reg("cat", function(a)
if #a == 0 then error("cat: need path") end
local path = resolvePath(a[1])
pendingCmd = { name = "cat", path = path,
prompt = curUser .. " " .. cwd .. "> cat " .. a[1] }
state = ST_WAIT_FS
fsReq("READ", path .. "|0|8192")
end, "Show file content")
reg("cd", function(a)
local path = a[1] and resolvePath(a[1]) or "/"
pendingCmd = { name = "cd", path = path,
prompt = curUser .. " " .. cwd .. "> cd " .. (a[1] or "") }
state = ST_WAIT_FS
fsReq("CHDIR", path)
end, "Change directory [path]")
reg("stat", function(a)
if #a == 0 then error("stat: need path") end
local path = resolvePath(a[1])
pendingCmd = { name = "stat", path = path,
prompt = curUser .. " " .. cwd .. "> stat " .. a[1] }
state = ST_WAIT_FS
fsReq("STAT", path)
end, "Show file metadata")
local function execCmd(cmdStr)
local parts = {}
for t in cmdStr:gmatch("[^ ]+") do
parts[#parts + 1] = t
end
if #parts == 0 then return end
local name = parts[1]
local args = {}
for i = 2, #parts do
args[#args + 1] = parts[i]
end
local entry = cmds[name]
if not entry then
qText(curUser .. " " .. cwd .. "> " .. cmdStr)
qErr("Unknown: " .. name .. ". Try 'help'.")
return
end
local ok, msg = pcall(entry.fn, args)
if not ok then
qText(curUser .. " " .. cwd .. "> " .. cmdStr)
qErr(tostring(msg))
end
end
local function handleFSResponse(op, data)
if not pendingCmd then return end
local cmd = pendingCmd
if op == "ERR" then
local code = data or "EIO"
local m = cmd.path
if code == "ENOENT" then
m = m .. ": No such file or directory"
elseif code == "EACCES" then
m = m .. ": Permission denied"
elseif code == "ENOTDIR" then
m = m .. ": Not a directory"
elseif code == "EISDIR" then
m = m .. ": Is a directory"
else
m = m .. ": " .. code
end
qText(cmd.prompt)
qErr(m)
pendingCmd = nil
state = ST_IDLE
return
end
local lines = { cmd.prompt }
if cmd.name == "ls" then
local showAll = (cmd.opts["-la"] or cmd.opts["-a"])
local names = {}
for n in data:gmatch("([^|]+)") do
names[#names + 1] = n
end
for _, n in ipairs(names) do
if n ~= "." and n ~= ".." or showAll then
lines[#lines + 1] = n
end
end
if #lines == 1 then lines[#lines + 1] = "(empty)" end
elseif cmd.name == "cat" then
if data and #data > 0 then
for line in data:gmatch("[^\n]+") do
lines[#lines + 1] = line
end
else
lines[#lines + 1] = "(empty)"
end
elseif cmd.name == "cd" then
cwd = data
lines[#lines + 1] = cwd
elseif cmd.name == "status" then
local parts = {}
for s in data:gmatch("([^|]+)") do
parts[#parts + 1] = s
end
local total = tonumber(parts[1]) or 8192
local used = tonumber(parts[2]) or 0
lines[#lines + 1] = "AtlasOS v2.0 User: " .. curUser .. "(" .. uid .. ")"
lines[#lines + 1] = "CWD: " .. cwd
lines[#lines + 1] = "Mem: " .. (total - used) .. "/" .. total .. " free"
elseif cmd.name == "stat" then
local parts = {}
for s in data:gmatch("([^|]+)") do
parts[#parts + 1] = s
end
if #parts >= 6 then
local typ = parts[2] == "d" and "directory" or "file"
lines[#lines + 1] = "Inode: " .. parts[1] .. " Type: " .. typ
lines[#lines + 1] = "Mode: " .. parts[3] .. " UID: " .. parts[4] .. " GID: " .. parts[5]
lines[#lines + 1] = "Size: " .. parts[6] .. " Modified: " .. (parts[7] or "?")
else
lines[#lines + 1] = data
end
end
queueLines(lines)
pendingCmd = nil
state = ST_IDLE
end
inp = {}
function upd()
if #outQueue > 0 then
local item = outQueue[1]
table.remove(outQueue, 1)
if item[1] == "t" then
out[1] = "OUT|" .. tostring(item[2])
elseif item[1] == "c" then
out[1] = "COL|" .. tostring(item[2])
elseif item[1] == "x" then
out[1] = "CLR"
end
end
if state == ST_IDLE then
if inp[1] ~= nil then
local op, cmd = parseMsg(tostring(inp[1]))
if op == "IN" and cmd and #cmd > 0 then
execCmd(cmd)
end
end
end
if state == ST_WAIT_FS and inp[2] ~= nil then
local resp = tostring(inp[2])
if #resp > 0 then
local op, rest = parseMsg(resp)
if op and rest then
local seqStr, data = parseMsg(rest)
local seq = tonumber(seqStr) or 0
if seq > lastRespSeq then
lastRespSeq = seq
handleFSResponse(op, data)
end
end
end
end
table.clear(inp)
end