v1
This commit is contained in:
236
atlas_os/CMC.lua
Normal file
236
atlas_os/CMC.lua
Normal file
@@ -0,0 +1,236 @@
|
||||
-- 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] = text_tx → IOC.in[2] (OUT|text)
|
||||
-- out[2] = clear_tx → IOC.in[3] (1 = clear)
|
||||
-- out[3] = color_tx → IOC.in[4] (R,G,B)
|
||||
-- out[4] = mem_tx → MMC.in[1] (FS request)
|
||||
|
||||
local state = 0
|
||||
local ST_IDLE = 0
|
||||
local ST_WAIT_FS = 1
|
||||
|
||||
local uid, gid, cwd, curUser = 0, 0, "/", "root"
|
||||
|
||||
local pendingCmd = nil
|
||||
local lastRespSeq = 0
|
||||
|
||||
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 printOut(t)
|
||||
out[1] = "OUT|" .. tostring(t)
|
||||
end
|
||||
|
||||
local function printErr(t)
|
||||
out[1] = "OUT|" .. tostring(t)
|
||||
end
|
||||
|
||||
local function doClear()
|
||||
out[2] = 1
|
||||
end
|
||||
|
||||
local function doColor(r, g, b)
|
||||
out[3] = r .. "," .. g .. "," .. b
|
||||
end
|
||||
|
||||
local function fsReq(op, arg)
|
||||
local msg = op
|
||||
if arg then msg = msg .. "|" .. arg end
|
||||
out[4] = msg
|
||||
end
|
||||
|
||||
local cmds = {}
|
||||
|
||||
local function reg(name, fn, desc)
|
||||
cmds[name] = { fn = fn, desc = desc }
|
||||
end
|
||||
|
||||
reg("help", function(a)
|
||||
local t = "AtlasOS v2.0 commands:\n"
|
||||
for n, e in pairs(cmds) do
|
||||
t = t .. " " .. n .. " " .. e.desc .. "\n"
|
||||
end
|
||||
return t
|
||||
end, "Show help")
|
||||
|
||||
reg("echo", function(a)
|
||||
if #a == 0 then error("echo: missing text") end
|
||||
return table.concat(a, " ")
|
||||
end, "Echo text")
|
||||
|
||||
reg("clear", function(a)
|
||||
doClear()
|
||||
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)))
|
||||
doColor(r, g, b)
|
||||
printOut("Color set to " .. r .. "," .. g .. "," .. b)
|
||||
end, "Set color R G B")
|
||||
|
||||
reg("status", function(a)
|
||||
return "AtlasOS v2.0 | User: " .. curUser .. "(" .. uid .. ") | CWD: " .. cwd
|
||||
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 = a[i]
|
||||
end
|
||||
end
|
||||
pendingCmd = { name = "ls", path = path, opts = opts }
|
||||
state = ST_WAIT_FS
|
||||
fsReq("LS", path)
|
||||
end, "List directory [path]")
|
||||
|
||||
reg("cat", function(a)
|
||||
if #a == 0 then error("cat: missing path") end
|
||||
pendingCmd = { name = "cat", path = a[1] }
|
||||
state = ST_WAIT_FS
|
||||
fsReq("READ", a[1] .. "|0|8192")
|
||||
end, "Show file content")
|
||||
|
||||
reg("cd", function(a)
|
||||
local path = a[1]
|
||||
if not path then path = "/" end
|
||||
pendingCmd = { name = "cd", path = path }
|
||||
state = ST_WAIT_FS
|
||||
fsReq("CHDIR", path)
|
||||
end, "Change directory [path]")
|
||||
|
||||
reg("stat", function(a)
|
||||
if #a == 0 then error("stat: missing path") end
|
||||
pendingCmd = { name = "stat", path = a[1] }
|
||||
state = ST_WAIT_FS
|
||||
fsReq("STAT", a[1])
|
||||
end, "Show file metadata")
|
||||
|
||||
local function execCmd(cmdStr)
|
||||
local parts = {}
|
||||
for t in cmdStr:gmatch("%S+") 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
|
||||
printErr("Unknown: " .. name .. ". Try 'help'.")
|
||||
return
|
||||
end
|
||||
|
||||
local ok, result = pcall(entry.fn, args)
|
||||
if not ok then
|
||||
printErr(tostring(result))
|
||||
elseif result ~= nil then
|
||||
printOut(result)
|
||||
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"
|
||||
if code == "ENOENT" then
|
||||
printErr(cmd.path .. ": No such file or directory")
|
||||
elseif code == "EACCES" then
|
||||
printErr(cmd.path .. ": Permission denied")
|
||||
elseif code == "ENOTDIR" then
|
||||
printErr(cmd.path .. ": Not a directory")
|
||||
elseif code == "EISDIR" then
|
||||
printErr(cmd.path .. ": Is a directory")
|
||||
else
|
||||
printErr(cmd.path .. ": " .. code)
|
||||
end
|
||||
pendingCmd = nil
|
||||
state = ST_IDLE
|
||||
return
|
||||
end
|
||||
|
||||
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
|
||||
local outLines = {}
|
||||
for _, n in ipairs(names) do
|
||||
if n ~= "." and n ~= ".." or showAll then
|
||||
outLines[#outLines + 1] = n
|
||||
end
|
||||
end
|
||||
if #outLines == 0 then outLines[1] = "(empty)" end
|
||||
printOut(table.concat(outLines, "\n"))
|
||||
elseif cmd.name == "cat" then
|
||||
printOut(data)
|
||||
elseif cmd.name == "cd" then
|
||||
cwd = data
|
||||
printOut(cwd)
|
||||
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"
|
||||
local out = "Inode: " .. parts[1] .. " Type: " .. typ .. "\n"
|
||||
out = out .. "Mode: " .. parts[3] .. " UID: " .. parts[4] .. " GID: " .. parts[5] .. "\n"
|
||||
out = out .. "Size: " .. parts[6] .. " Modified: " .. (parts[7] or "?")
|
||||
printOut(out)
|
||||
else
|
||||
printOut(data)
|
||||
end
|
||||
end
|
||||
|
||||
pendingCmd = nil
|
||||
state = ST_IDLE
|
||||
end
|
||||
|
||||
inp = {}
|
||||
|
||||
function upd()
|
||||
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
|
||||
39
atlas_os/IOC.lua
Normal file
39
atlas_os/IOC.lua
Normal file
@@ -0,0 +1,39 @@
|
||||
-- IOC.lua — I/O Controller (AtlasOS Terminal v2.0)
|
||||
-- out[1] = display_text → Terminal
|
||||
-- out[2] = display_clear → Terminal
|
||||
-- out[3] = display_color → Terminal
|
||||
-- out[4] = cmd_tx → CMC.in[1] (IN|command)
|
||||
-- in[1] = user_input ← Terminal
|
||||
-- in[2] = text_rx ← CMC.out[1] (OUT|text)
|
||||
-- in[3] = clear_rx ← CMC.out[2] (1 = clear)
|
||||
-- in[4] = color_rx ← CMC.out[3] (R,G,B)
|
||||
|
||||
local txActive = false
|
||||
|
||||
inp = {}
|
||||
|
||||
function upd()
|
||||
if inp[1] ~= nil then
|
||||
out[4] = "IN|" .. tostring(inp[1])
|
||||
txActive = true
|
||||
elseif txActive then
|
||||
out[4] = ""
|
||||
txActive = false
|
||||
end
|
||||
|
||||
if inp[2] ~= nil then
|
||||
local msg = tostring(inp[2])
|
||||
local _, arg = msg:match("^(%w+)%|(.+)$")
|
||||
if arg then out[1] = arg end
|
||||
end
|
||||
|
||||
if inp[3] ~= nil and tonumber(inp[3]) == 1 then
|
||||
out[2] = 1
|
||||
end
|
||||
|
||||
if inp[4] ~= nil then
|
||||
out[3] = tostring(inp[4])
|
||||
end
|
||||
|
||||
table.clear(inp)
|
||||
end
|
||||
273
atlas_os/MMC.lua
Normal file
273
atlas_os/MMC.lua
Normal file
@@ -0,0 +1,273 @@
|
||||
-- MMC.lua — Memory/FS Controller (AtlasOS v2.0)
|
||||
-- in[1] = mem_rx ← CMC.out[4] (FS request)
|
||||
-- in[3] = mem_read ← Memory.out[1]
|
||||
-- out[1] = mem_tx → CMC.in[2] (FS response)
|
||||
-- out[5] = mem_data → Memory.in[1]
|
||||
-- out[6] = mem_write → Memory.in[2]
|
||||
|
||||
local SEG_SIZE = 8192
|
||||
local MAX_INODES = 64
|
||||
local ROOT_INO = 1
|
||||
|
||||
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 split(str, sep)
|
||||
local t = {}
|
||||
if not str then return t end
|
||||
for s in str:gmatch("[^" .. sep .. "]+") do
|
||||
t[#t + 1] = s
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local inodes = {}
|
||||
local dirs = {}
|
||||
local files = {}
|
||||
local users = {}
|
||||
local sb = { seg_size = SEG_SIZE, max_inodes = MAX_INODES, root_ino = ROOT_INO }
|
||||
|
||||
local function fsSerialize()
|
||||
local lines = {}
|
||||
lines[#lines + 1] = "V|1"
|
||||
lines[#lines + 1] = "S|" .. sb.seg_size .. "|" .. sb.max_inodes .. "|" .. sb.root_ino
|
||||
for _, u in pairs(users) do
|
||||
lines[#lines + 1] = "U|" .. u.uid .. "|" .. u.name .. "|" .. u.gid .. "|" .. u.home .. "|" .. (u.hash or "-")
|
||||
end
|
||||
for i = 1, MAX_INODES do
|
||||
local ino = inodes[i]
|
||||
if ino then
|
||||
lines[#lines + 1] = "I|" .. ino.ino .. "|" .. ino.typ .. "|" .. ino.mode .. "|" .. ino.uid .. "|" .. ino.gid .. "|" .. ino.size .. "|" .. ino.mtime .. "|"
|
||||
if ino.typ == "d" and dirs[ino.ino] then
|
||||
local entries = {}
|
||||
for _, e in ipairs(dirs[ino.ino]) do
|
||||
entries[#entries + 1] = e.name .. ":" .. e.ino
|
||||
end
|
||||
lines[#lines + 1] = "D|" .. ino.ino .. "|" .. table.concat(entries, "|")
|
||||
elseif ino.typ == "f" and files[ino.ino] then
|
||||
lines[#lines + 1] = "F|" .. ino.ino .. "|" .. files[ino.ino]
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.concat(lines, "\n")
|
||||
end
|
||||
|
||||
local function fsDeserialize(str)
|
||||
inodes = {}
|
||||
dirs = {}
|
||||
files = {}
|
||||
users = {}
|
||||
sb = { seg_size = SEG_SIZE, max_inodes = MAX_INODES, root_ino = ROOT_INO }
|
||||
if not str or #str == 0 then return false end
|
||||
for line in str:gmatch("[^\n]+") do
|
||||
local typ = line:sub(1, 1)
|
||||
local rest = line:sub(3)
|
||||
if typ == "V" then
|
||||
elseif typ == "S" then
|
||||
local parts = split(rest, "|")
|
||||
if #parts >= 3 then
|
||||
sb.seg_size = tonumber(parts[1]) or SEG_SIZE
|
||||
sb.max_inodes = tonumber(parts[2]) or MAX_INODES
|
||||
sb.root_ino = tonumber(parts[3]) or ROOT_INO
|
||||
end
|
||||
elseif typ == "U" then
|
||||
local parts = split(rest, "|")
|
||||
if #parts >= 4 then
|
||||
users[tonumber(parts[1])] = { uid = tonumber(parts[1]), name = parts[2], gid = tonumber(parts[3]), home = parts[4], hash = parts[5] or "-" }
|
||||
end
|
||||
elseif typ == "I" then
|
||||
local parts = split(rest, "|")
|
||||
if #parts >= 7 then
|
||||
local ino = tonumber(parts[1])
|
||||
inodes[ino] = { ino = ino, typ = parts[2], mode = parts[3], uid = tonumber(parts[4]), gid = tonumber(parts[5]), size = tonumber(parts[6]), mtime = tonumber(parts[7]) or 0 }
|
||||
end
|
||||
elseif typ == "D" then
|
||||
local parts = split(rest, "|")
|
||||
if #parts >= 2 then
|
||||
local ino = tonumber(parts[1])
|
||||
local entries = {}
|
||||
for i = 2, #parts do
|
||||
local eparts = split(parts[i], ":")
|
||||
if #eparts >= 2 then
|
||||
entries[#entries + 1] = { name = eparts[1], ino = tonumber(eparts[2]) }
|
||||
end
|
||||
end
|
||||
dirs[ino] = entries
|
||||
end
|
||||
elseif typ == "F" then
|
||||
local parts = split(rest, "|")
|
||||
if #parts >= 2 then
|
||||
local ino = tonumber(parts[1])
|
||||
local content = parts[2] or ""
|
||||
for i = 3, #parts do
|
||||
content = content .. "|" .. parts[i]
|
||||
end
|
||||
files[ino] = content
|
||||
end
|
||||
end
|
||||
end
|
||||
return next(inodes) ~= nil
|
||||
end
|
||||
|
||||
local function findInodeByPath(path)
|
||||
if not path or #path == 0 then path = "/" end
|
||||
local parts = split(path, "/")
|
||||
local cur = inodes[sb.root_ino]
|
||||
if not cur then return nil end
|
||||
if path == "/" then return cur end
|
||||
local curIno = sb.root_ino
|
||||
for i = 1, #parts do
|
||||
local name = parts[i]
|
||||
if #name > 0 then
|
||||
local found = false
|
||||
local dentries = dirs[curIno]
|
||||
if dentries then
|
||||
for _, e in ipairs(dentries) do
|
||||
if e.name == name then
|
||||
curIno = e.ino
|
||||
cur = inodes[curIno]
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if not found then return nil end
|
||||
end
|
||||
end
|
||||
return cur and inodes[curIno] or nil
|
||||
end
|
||||
|
||||
local respSeq = 0
|
||||
|
||||
local function processReq(req)
|
||||
local op, rest = parseMsg(req)
|
||||
if not op then return "ERR|EINVAL" end
|
||||
respSeq = respSeq + 1
|
||||
local seqStr = tostring(respSeq) .. "|"
|
||||
|
||||
if op == "STAT" then
|
||||
local path = rest or "/"
|
||||
local ino = findInodeByPath(path)
|
||||
if not ino then return "ERR|" .. seqStr .. "ENOENT" end
|
||||
return "OK|" .. seqStr .. ino.ino .. "|" .. ino.typ .. "|" .. ino.mode .. "|" .. ino.uid .. "|" .. ino.gid .. "|" .. ino.size .. "|" .. ino.mtime
|
||||
|
||||
elseif op == "READ" then
|
||||
local parts = split(rest or "", "|")
|
||||
local path = parts[1] or ""
|
||||
local offset = tonumber(parts[2]) or 0
|
||||
local len = tonumber(parts[3]) or 8192
|
||||
local ino = findInodeByPath(path)
|
||||
if not ino then return "ERR|" .. seqStr .. "ENOENT" end
|
||||
if ino.typ ~= "f" then return "ERR|" .. seqStr .. "EISDIR" end
|
||||
local content = files[ino.ino] or ""
|
||||
local data = content:sub(offset + 1, offset + len)
|
||||
return "OK|" .. seqStr .. data
|
||||
|
||||
elseif op == "LS" then
|
||||
local path = rest or "/"
|
||||
local ino = findInodeByPath(path)
|
||||
if not ino then return "ERR|" .. seqStr .. "ENOENT" end
|
||||
if ino.typ ~= "d" then return "ERR|" .. seqStr .. "ENOTDIR" end
|
||||
local dentries = dirs[ino.ino]
|
||||
if not dentries then return "OK|" .. seqStr end
|
||||
local names = {}
|
||||
for _, e in ipairs(dentries) do
|
||||
names[#names + 1] = e.name
|
||||
end
|
||||
return "OK|" .. seqStr .. table.concat(names, "|")
|
||||
|
||||
elseif op == "CHDIR" then
|
||||
local path = rest or "/"
|
||||
local ino = findInodeByPath(path)
|
||||
if not ino then return "ERR|" .. seqStr .. "ENOENT" end
|
||||
if ino.typ ~= "d" then return "ERR|" .. seqStr .. "ENOTDIR" end
|
||||
return "OK|" .. seqStr .. path
|
||||
|
||||
elseif op == "WRITE" then
|
||||
return "ERR|" .. seqStr .. "EROFS"
|
||||
end
|
||||
|
||||
return "ERR|" .. seqStr .. "EINVAL"
|
||||
end
|
||||
|
||||
local function defaultFS()
|
||||
inodes = {}
|
||||
dirs = {}
|
||||
files = {}
|
||||
users = {}
|
||||
|
||||
users[0] = { uid = 0, name = "root", gid = 0, home = "/", hash = "-" }
|
||||
users[100] = { uid = 100, name = "captain", gid = 10, home = "/home/captain", hash = "-" }
|
||||
|
||||
local function mkIno(num, typ, mode, uid, gid, size)
|
||||
inodes[num] = { ino = num, typ = typ, mode = mode, uid = uid, gid = gid, size = size or 0, mtime = 0 }
|
||||
end
|
||||
|
||||
local function mkDir(num, entries)
|
||||
dirs[num] = entries
|
||||
end
|
||||
|
||||
local function mkFile(num, content)
|
||||
files[num] = content or ""
|
||||
local sz = #(content or "")
|
||||
if inodes[num] then inodes[num].size = sz end
|
||||
end
|
||||
|
||||
mkIno(1, "d", "755", 0, 0, 0)
|
||||
mkDir(1, { { name = ".", ino = 1 }, { name = "..", ino = 1 }, { name = "home", ino = 2 }, { name = "etc", ino = 4 } })
|
||||
|
||||
mkIno(2, "d", "755", 0, 0, 0)
|
||||
mkDir(2, { { name = ".", ino = 2 }, { name = "..", ino = 1 }, { name = "captain", ino = 5 } })
|
||||
|
||||
mkIno(5, "d", "700", 100, 10, 0)
|
||||
mkDir(5, { { name = ".", ino = 5 }, { name = "..", ino = 2 }, { name = "readme.txt", ino = 3 } })
|
||||
|
||||
mkIno(3, "f", "644", 100, 10, 0)
|
||||
mkFile(3, "Welcome to AtlasOS v2.0!\n")
|
||||
|
||||
mkIno(4, "d", "755", 0, 0, 0)
|
||||
mkDir(4, { { name = ".", ino = 4 }, { name = "..", ino = 1 } })
|
||||
|
||||
local str = fsSerialize()
|
||||
out[5] = str
|
||||
out[6] = 1
|
||||
end
|
||||
|
||||
local fsLoaded = false
|
||||
local prevReq = ""
|
||||
local writePending = false
|
||||
|
||||
inp = {}
|
||||
|
||||
function upd()
|
||||
if not fsLoaded then
|
||||
local memStr = inp[3] or ""
|
||||
if #memStr > 0 and fsDeserialize(memStr) then
|
||||
fsLoaded = true
|
||||
else
|
||||
fsLoaded = true
|
||||
defaultFS()
|
||||
writePending = true
|
||||
end
|
||||
end
|
||||
|
||||
if writePending then
|
||||
local str = fsSerialize()
|
||||
out[5] = str
|
||||
out[6] = 1
|
||||
writePending = false
|
||||
end
|
||||
|
||||
if inp[1] ~= nil then
|
||||
local req = tostring(inp[1])
|
||||
if #req > 0 and req ~= prevReq then
|
||||
prevReq = req
|
||||
local resp = processReq(req)
|
||||
out[1] = resp
|
||||
end
|
||||
end
|
||||
|
||||
table.clear(inp)
|
||||
end
|
||||
152
atlas_os/docs/wiring.md
Normal file
152
atlas_os/docs/wiring.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Схема соединений AtlasOS v2.0
|
||||
|
||||
## Компоненты
|
||||
|
||||
| Компонент | Кол-во | Назначение |
|
||||
|---|---|---|
|
||||
| MicroLua (IOC) | 1 | I/O Controller — терминал |
|
||||
| MicroLua (CMC) | 1 | Command Controller — shell |
|
||||
| MicroLua (MMC) | 1 | Memory/FS Controller — ФС |
|
||||
| Terminal | 1 | текстовый дисплей |
|
||||
| Memory | 1 | хранилище 8192 символа |
|
||||
|
||||
## Полная схема
|
||||
|
||||
```
|
||||
Терминал (Keyboard/Button) Терминал (Display)
|
||||
│ text ▲
|
||||
▼ │ text
|
||||
┌──────────┐ ┌──────────┐ │ clear
|
||||
│ │─────────▶ │──────┤ color
|
||||
│ IOC │ IN|cmd │ CMC │ │
|
||||
│ │◀────────│ │◀─────┤
|
||||
└──────────┘ OUT|text│ │ 1 │
|
||||
▲ clear=1 │ │◀─────┤
|
||||
│ R,G,B └────▲────┘ R,G,B│
|
||||
│ │ │
|
||||
│ LS|path │
|
||||
│ READ|... │
|
||||
│ │ │
|
||||
│ ┌────▼────┐ │
|
||||
│ │ │ │
|
||||
│ │ MMC │ │
|
||||
│ │ │ │
|
||||
│ └──┬──┬───┘ │
|
||||
│ │ │ │
|
||||
│ data │ │ write(1) │
|
||||
│ ▼ ▼ │
|
||||
│ ┌────────┐ │
|
||||
│ │ Memory │ │
|
||||
│ │ 8192 │ │
|
||||
│ └───┬────┘ │
|
||||
│ │ │
|
||||
└──────────────────┴────────────┘
|
||||
```
|
||||
|
||||
## Соединения проводом
|
||||
|
||||
### IOC ↔ Терминал
|
||||
|
||||
| Пин IOC | → / ← | Пин Terminal | Назначение |
|
||||
|---|---|---|---|
|
||||
| IOC.out[1] | → | Display.in (text) | текст на экран |
|
||||
| IOC.out[2] | → | Display.in (clear) | очистка (1) |
|
||||
| IOC.out[3] | → | Display.in (color) | цвет R,G,B |
|
||||
| IOC.in[1] | ← | Keyboard.out (text) | ввод пользователя |
|
||||
|
||||
### IOC ↔ CMC
|
||||
|
||||
| Пин IOC | → / ← | Пин CMC | Назначение |
|
||||
|---|---|---|---|
|
||||
| IOC.out[4] | → | CMC.in[1] | команда `IN|cmd` |
|
||||
| CMC.out[1] | → | IOC.in[2] | текст `OUT|text` |
|
||||
| CMC.out[2] | → | IOC.in[3] | очистка (1) |
|
||||
| CMC.out[3] | → | IOC.in[4] | цвет `R,G,B` |
|
||||
|
||||
### CMC ↔ MMC
|
||||
|
||||
| Пин CMC | → / ← | Пин MMC | Назначение |
|
||||
|---|---|---|---|
|
||||
| CMC.out[4] | → | MMC.in[1] | запрос `OP|args` |
|
||||
| MMC.out[1] | → | CMC.in[2] | ответ `OK|seq|...` |
|
||||
|
||||
### MMC ↔ Memory
|
||||
|
||||
| Пин MMC | → / ← | Пин Memory | Назначение |
|
||||
|---|---|---|---|
|
||||
| MMC.out[5] | → | Memory.in[1] | данные для записи |
|
||||
| MMC.out[6] | → | Memory.in[2] | сигнал записи (1) |
|
||||
| Memory.out[1] | → | MMC.in[3] | чтение данных |
|
||||
|
||||
## Протокол шины
|
||||
|
||||
### IOC → CMC (CMD)
|
||||
|
||||
```
|
||||
IN|команда
|
||||
```
|
||||
|
||||
### CMC → IOC (CMD)
|
||||
|
||||
```
|
||||
OUT|текст — вывести текст
|
||||
1 на out[2] — очистить экран
|
||||
R,G,B на out[3] — установить цвет
|
||||
```
|
||||
|
||||
### CMC → MMC (MEM)
|
||||
|
||||
```
|
||||
STAT|путь — метаданные
|
||||
READ|путь|смещение|длина — чтение файла
|
||||
LS|путь — список каталога
|
||||
CHDIR|путь — смена директории (проверка)
|
||||
WRITE|путь|смещение|данные — запись (пока EROFS)
|
||||
```
|
||||
|
||||
### MMC → CMC (MEM)
|
||||
|
||||
```
|
||||
OK|seq|данные
|
||||
ERR|seq|код_ошибки
|
||||
```
|
||||
|
||||
Коды ошибок: `ENOENT`, `EACCES`, `ENOTDIR`, `EISDIR`, `EROFS`, `EINVAL`
|
||||
|
||||
## Последовательность запуска
|
||||
|
||||
1. Загрузить код MMC.lua в контроллер MMC
|
||||
2. Загрузить код CMC.lua в контроллер CMC
|
||||
3. Загрузить код IOC.lua в контроллер IOC
|
||||
4. Соединить провода по схеме выше
|
||||
5. При первом запуске MMC инициализирует пустую ФС и запишет в Memory
|
||||
6. Подать сигнал на Terminal — появится приглашение
|
||||
|
||||
## Пример сессии
|
||||
|
||||
```
|
||||
> ls
|
||||
home
|
||||
etc
|
||||
> ls /home/captain
|
||||
readme.txt
|
||||
> cat /home/captain/readme.txt
|
||||
Welcome to AtlasOS v2.0!
|
||||
> cd /home/captain
|
||||
/home/captain
|
||||
> status
|
||||
AtlasOS v2.0 | User: root(0) | CWD: /home/captain
|
||||
> color 0 255 128
|
||||
Color set to 0,255,128
|
||||
> help
|
||||
AtlasOS v2.0 commands:
|
||||
help Show help
|
||||
echo Echo text
|
||||
clear Clear screen
|
||||
color Set color R G B
|
||||
status System status
|
||||
ls List directory [path]
|
||||
cat Show file content
|
||||
cd Change directory [path]
|
||||
stat Show file metadata
|
||||
```
|
||||
Reference in New Issue
Block a user