// THIS IS A BETA AND BUGS MAY BE PRESENT
// DO NOT USE THIS, USE EITHER URCL OS v2.3.2 or v3.0.0 (prerelease)

// URCL OS v2

// Whats new in v2:
// Rewrite of all functions
// New intermediate language
// Terminal upgrades (more to come as well)
// Directories
// New file header format (easier to access information and scales better)

// Included programs: ls (0 or /0/0), cd (1 or /0/1)

// URCL HEADER
BITS >= 16
MINREG 4
MINHEAP 200
MINSTACK 6


.Initialize
// Instruction to Address Table
.InstructionTable
STR M0, 0
STR M1, .Immediate // 1
STR M2, .Add // 2
STR M3, .Subtract // 3
STR M4, .Multiply // 4
STR M5, .Divide // 5
STR M6, .Modulo // 6
STR M7, .Xor // 7
STR M8, .And // 8
STR M9, .Or // 9
STR M10, .ShiftLeft // 10
STR M11, .ShiftRight // 11
STR M12, .BranchIfZero // 12
STR M13, .BranchIfNotZero // 13
STR M14, .BranchIfEqual // 14
STR M15, .BranchIfNotEqual // 15
STR M16, .BranchIfLessThan // 16
STR M17, .BranchIfGreaterThan // 17
STR M18, .Jump // 18
STR M19, .LoadMemory // 19
STR M20, .StoreMemory // 20
STR M21, .Move // 21
STR M22, .Push // 22
STR M23, .Pop // 23
STR M24, .Call // 24
STR M25, .Return // 25
STR M26, .Add2Word // 26
STR M27, .Increment // 27
STR M28, .Decrement // 28
STR M29, .SystemCall // 29
STR M30, .Halt // 30
STR M31, .Error // 31

//* Interpreter Variables *//
.Pointer // Stores the address of the next instruction to be executed
STR M32, 0x0000

.ProgramLimit // Stores the limit of the program currently executed (global address) (inclusive)
STR M33, 0x0000

//* File Scan Variables *//
.MaxFiles // Stores the maximum amount of files in the root directory multiplied by 4 (Total = maximum amount of file info words)
STR M34, 0x0020

//* Other OS Variables *//
.VirtualRegisters
STR M35, 0 // R0 (Set to 0 before each program)
STR M36, 0 // R1
STR M37, 0 // R2
STR M38, 0 // R3
STR M39, 0 // R4 //.files
STR M40, 0 // R5
STR M41, 0 // R6
STR M42, 0 // R7
STR M43, 0 // R8
STR M44, 0 // R9
STR M45, 0 // R10
STR M46, 0 // R11
STR M47, 0 // R12
STR M48, 0 // R13
STR M49, 0 // R14
.JumpDest
STR M50, 0 // Jump Dest (R15)

.Stackpointer
STR M51, SP

.CurrentWorkingDirectory
STR M52, 0 //.files

.RootDirectory
STR M53, 0 //.files

.StartupProgram // Coming soon
STR M54, 0

//* Constants *//
.ProgramMemoryLimit // Stores the limit of the memory for programs (inclusive)
STR M55, 0xFFFF // 65535

.Runram // Stores whether cpu is runram (1 if runram, 0 if runrom)
STR M56, 0

.Bits // Stores the amount of bits of the cpu
//STR M74, 0 //incase &BITS does not work in compiler/emulator (cough cough mod)
STR M57, &BITS

.MemoryAvailable // Stores the amount of memory available to the cpu
STR M58, 0xFFFF

.Mem0
STR M59, M78

// System Calls
.SystemCalls
STR M60, .Exit // 0
STR M61, .DriveAddr // 1
STR M62, .DriveRead // 2
STR M63, .DriveWrite // 3
STR M64, .DrivePage // 4
STR M65, .Profile // 5
STR M66, .CallProgram // 6
STR M67, .CallProgramFromDirectory // 7
STR M68, .DeviceNumber // 8
STR M69, .InChar // 9
STR M70, .OutChar // 10
STR M71, .InString // 11 (Coming soon)
STR M72, .OutString // 12 (Coming soon)
STR M73, .InHex // 13
STR M74, .OutHex // 14
STR M75, .ChangeWorkingDirectory // 15
STR M76, .ChangeWorkingDirectoryFromCurrent // 16

STR M77, 0

JMP .Shell
.ShellEnd
POP R1
BNE .ShellEnd, R1, 0
.Shell
OUT %TEXT, 0x24
OUT %TEXT, 0x20
PSH 0
MOV R2, SP
.ShellLoop
IN R1, %TEXT
OUT %TEXT, R1
BRE .Backspace, R1, 0x8
PSH R1
BNE .ShellLoop, R1, 0xA
//if first char null or : exit
LOD R1, M52 //save wd so can reset
PSH R1
DEC R2, R2
LOD R3, R2
BRE .ShellEnd, R3, 0xA // Exit if null
BRE .ShellEnd, R3, 0x3A // Exit if ':'
BRE .ChangeToRoot, R3, 0x2F
BRE .FileFindNoSkip, R3, 0x2E
//if first char not / look in /bin (/0)
LOD R4, M53
ADD R4, R4, 3
OUT %ADDR, R4
IN R4, %BUS
STR M52, R4
JMP .FileFindLoopSlash
//if first char / follow directory starting from root
.ChangeToRoot
LOD R4, M53
STR M52, R4
//if first char . follow directory from current
//////////////////////////////////////
.FileFindNoSkip
DEC R2, R2
LOD R3, R2 //R3 = char
.FileFindLoopSlash
BRE .FileFindNoSkip, R3, 0x2F //ignore if slash (only occurs if ./ or // at begininig)
LOD R1, M52
.FileFindLoop
OUT %ADDR, R1
IN R4, %BUS
BRZ .Error, R4 
AND R4, R4, 0xFF
BRE .FileFoundAttempt, R4, R3
ADD R1, R1, 4
JMP .FileFindLoop



.FileFoundAttempt
//get length & address of first char
PSH R2 //original SP
OUT %ADDR, R1
IN R4, %BUS
BSR R4, R4, 8 //name length (R4 max size is 255)
ADD R1, R1, 3 //add 3 to get to address word 
PSH R1 //address word
OUT %ADDR, R1
IN R1, %BUS
SUB R1, R1, R4 //get address of second name char
.LodNextChar
OUT %ADDR, R1
IN R3, %BUS
//load next char
PSH R1
DEC R2, R2
LOD R1, R2
PSH R2
//if R4 == 0 && nextchar == (\n || ' ') then file found
SETE R2, R1, 0xA
ADD R2, R2, R4
BRE .FileFound, R2, &MAX
SETE R2, R1, 0x20
ADD R2, R2, R4
BRE .FileFound R2, &MAX
//if R4 == 0 && nextchar == / then folder found
SETE R2, R1, 0x2F
ADD R2, R2, R4
BRE .FolderFound, R2, &MAX
//if (R4 != 0 && nextchar == (/ || \n || ' ')) || (R4 == 0 && nextchar != (/ || \n || ' ')) then attempt failed
SETNE R2, R4, 0 //explaination of this condition check: if R4 is not 0, R2 = -1, if R2 + nextchar produces a carry (R2 = -1 and nextchar != 0) then R2 = -1, then after adding next char check if the result is one less than expected char
SETC R2, R2, R1
ADD R2, R2, R1
BRE .AttemptFailed, R2, 9
BRE .AttemptFailed, R2, 0x2E
BRE .AttemptFailed, R2, 0x1F
PSH R3
SETE R2, R1, 0x2F //nextchar == (/ || \n || ' ')
SETE R3, R1, 0xA
OR R2, R2, R3
SETE R3, R1, 0x20
OR R2, R2, R3
SETE R3, R4, 0 //R4 == 0
NOT R2, R2 //nextchar != (/ || \n || ' ') &MAX
BRC .AttemptFailed, R2, R3 //if both are true
POP R3
//if R3 != nextchar then attempt failed
BNE .AttemptFailed, R3, R1
//else continue depth
POP R2
POP R1
DEC R4, R4
INC R1, R1
JMP .LodNextChar

.FolderFound
POP R2
POP R0
POP R1
POP R0
OUT %ADDR, R1
IN R1, %BUS
DEC R2, R2
LOD R3, R2
JMP .FileFindLoop

.AttemptFailed
//pop useless crap
POP R0 
POP R0 
POP R1
POP R2 //Reset SP
//check next file in dir
INC R1, R1 //R1 points to next file
OUT %ADDR, R1
IN R3, %BUS
BNZ .FileFoundAttempt, R3

.FileFound
//victory royale condition
POP R0 //pointer to 0xa
POP R1
POP R3 //address pointer
POP R0
//R4 == 0 at this point meaning that R1 must equal start of file
MOV R2, R1 //start of the file
POP R4 //R4 = original wd root
STR M39, R4
STR .CurrentWorkingDirectory, R4
DEC R4, R3
POP R0
.RunProgram
LOD R1, M59
STR M32, R1
DEC R1, R1
    //set .ProgramLimit to the limit of the file (inclusive) (read description of the variable)
    LOD R3, M77
    BRE .InterpretationLoop, R3, R2
    //check if program cached already ^
    OUT %ADDR, R4
    IN R4, %BUS //get length
    ADD R3, R1, R4
    INC R3, R3
    STR M33, R3
    .LoadProgram
    MOV R4, R1
    STR R4, R2
    INC R4, R4
    .CacheLoop
    OUT %ADDR, R2
    IN R3, %BUS
    STR R4, R3
    INC R2, R2
    INC R4, R4
    BRG .CacheLoop, R1, R2
  
.InterpretationLoop // The loop that is ran for running a program (calling .Execute per instruction)
    // Prepare for next program being executed (reset variables)
    STR M35, 0 // Set R0 to 0
    // R1 = Pointer
    // R3 = Temp
    .Loop3
        INC R1, R1 // Update PC
        // Check if reached the end of a program
        LOD R3, M33 //////////////
        BRG .ShellEnd, R1, R3
        .SkipOutput
        // R4 = full operation
        // R2 = instruction
        LOD R4, R1 // R2 = mem[R1(pointer)]
        AND R2, R4, 0x001F // instruction = R2
        OUT %NUMB, R2
        .Execute
            BRZ .Loop3, R2 //exit if no instruction
            ADD R2, R2, M0 //add offset to instruction table
            LOD R2, R2 //load jump address
            JMP R2 //call instruction

    .EndLoop3

//* INSTRUCTIONS *//

// 11112222000IIIII
// I = instruction
// 1, 2 = operand
// 0 = only used in system call
.Immediate
AND R2, R4, 0xF000 // R2 = op1
BSR R2, R2, 12
INC R1, R1 // Inc pointer
LOD R3, R1 // Load the immediate
ADD R2, M35, R2 // Add offset
STR R2, R3 // Store the immediate
JMP .Loop3

.Add
CAL .Get2op
ADD R4, R4, R3 // Do the addition
STR R2, R4
JMP .Loop3

.Subtract
CAL .Get2op
SUB R4, R4, R3 // Do the subtraction
STR R2, R4
JMP .Loop3

.Multiply
CAL .Get2op
MLT R4, R4, R3 // Do the multiplication
STR R2, R4
JMP .Loop3

.Divide
CAL .Get2op
DIV R4, R4, R3 // Do the division
STR R2, R4
JMP .Loop3

.Modulo
CAL .Get2op
MOD R4, R4, R3 // Do the modulo
STR R2, R4
JMP .Loop3

.Xor
CAL .Get2op
XOR R4, R4, R3 // Do the bitwise xor
STR R2, R4
JMP .Loop3

.And
CAL .Get2op
AND R4, R4, R3 // Do the bitwise and
STR R2, R4
JMP .Loop3

.Or
CAL .Get2op
OR R4, R4, R3 // Do the bitwise or
STR R2, R4
JMP .Loop3

.ShiftLeft
CAL .Get2op
BSL R3, R4, R3
STR R2, R3
JMP .Loop3

.ShiftRight
CAL .Get2op
BSR R3, R4, R3
STR R2, R3
JMP .Loop3

.BranchIfZero
CAL .Get2op
BNZ .BRZend, R3
LOD R3, M32
ADD R4, R4, R3
MOV R1, R4
DEC R1, R1
.BRZend
JMP .Loop3

.BranchIfNotZero
CAL .Get2op
BRZ .BNZend, R3
LOD R3, M32
ADD R4, R4, R3
MOV R1, R4
DEC R1, R1
.BNZend
JMP .Loop3

.BranchIfEqual
CAL .Get2op
BNE .BREend, R4, R3
LOD R3, M50
LOD R4, M32
ADD R1, R3, R4
DEC R1, R1
.BREend
JMP .Loop3

.BranchIfNotEqual
CAL .Get2op
BRE .BNEend, R4, R3
LOD R3, M50
LOD R4, M32
ADD R1, R3, R4
DEC R1, R1
.BNEend
JMP .Loop3

.BranchIfGreaterThan
CAL .Get2op
BLE .BRGend, R4, R3
LOD R3, M50
LOD R4, M32
ADD R1, R3, R4
DEC R1, R1
.BRGend
JMP .Loop3

.BranchIfLessThan
CAL .Get2op
BGE .BRLend, R4, R3
LOD R3, M50
LOD R4, M32
ADD R1, R3, R4
DEC R1, R1
.BRLend
JMP .Loop3

.Jump
CAL .Get1op
LOD R4, M32
ADD R1, R4, R3
DEC R1, R1
JMP .Loop3

.LoadMemory
CAL .Get2op
LOD R3, R3
STR R2, R3
JMP .Loop3

.StoreMemory
CAL .Get2op
STR R4, R3
JMP .Loop3

.Move
CAL .Get2op
STR R2, R3
JMP .Loop3

.Push
CAL .Get1op
PSH R3
STR M51, SP
JMP .Loop3

.Pop
CAL .Get1op
POP R3
STR R2, R3
STR M51, SP
JMP .Loop3

.Call
CAL .Get1op
LOD R4, M32
ADD R4, R4, R3
MOV R1, R3
DEC R1, R1
PSH R4
JMP .Loop3

.Return
POP R1
JMP .Loop3

.Add2Word //award for most painful instruction to impliment
AND R2, R4, 0xF000 // R2 = op1
BSR R2, R2, 12
ADD R2, R2, M35
PSH R2
LOD R2, R2
LLOD R3, R2, 1
AND R4, R4, 0x0F00 // R4 = op2
BSR R4, R4, 8
ADD R4, R4, M35
PSH R4
LOD R4, R4
BNC .AddNoCarry, R4, R2
INC R3, R3
.AddNoCarry
ADD R4, R4, R2
POP R2
LLOD R2, R2, 1
ADD R3, R3, R2
POP R2
STR R2, R4
LSTR R2, 1, R3
JMP .Loop3

.Increment
CAL .Get1op
INC R3, R3
STR R2, R3
JMP .Loop3

.Decrement
CAL .Get1op
DEC R3, R3
STR R2, R3
JMP .Loop3

.SystemCall
AND R2, R4, 0x0FE0 // R2 = op1
BSR R2, R2, 5
ADD R2, R2, M60 // Add offset
LOD R3, R2 // Load jump address
JMP R3

.Halt
JMP .ShellEnd

//System Calls
.Exit
HLT

.DriveAddr
CAL .Get1op
OUT %ADDR, R3
JMP .Loop3

.DriveRead
CAL .Get1op
IN R3, %BUS
STR R2, R3
JMP .Loop3

.DriveWrite
CAL .Get1op
OUT %BUS, R3
JMP .Loop3

.DrivePage
CAL .Get1op
OUT %PAGE, R3
JMP .Loop3

.Profile
CAL .Get1op
OUT %PROFILE, R3
JMP .Loop3

.CallProgram
CAL .Get1op
.CallprogramOpPresent
STR M32, R3 //set pointer
MOV R1, R3 //set program counter
STR M35, 0 //set 0 reg
JMP .Loop3

.CallProgramFromDirectory
CAL .Get1op
BSL R3, R3, 2 //mult by 4
ADD R3, R3, 3
LOD R4, M52 //.CurrentWorkingDirectory load current working directory
ADD R3, R3, R4 //add offset
LOD R3, R3 //load address
JMP .CallprogramOpPresent

.DeviceNumber
CAL .Get1op
OUT %PROFILE, R3
JMP .Loop3

.InChar
AND R2, R4, 0xF000 // R2 = op1
BSR R2, R2, 12
ADD R2, R2, M35
IN R3, %TEXT
STR R2, R3
JMP .Loop3

.OutChar
CAL .Get1op
OUT %TEXT, R3
JMP .Loop3

.InString // Coming soon
.OutString // Coming soon
JMP .Error

.InHex
AND R2, R4, 0xF000 // R2 = op1
BSR R2, R2, 12
ADD R2, R2, M35
PSH R2
IMM R4, 16
IMM R2, 0
.Loop4
SUB R4, R4, 4
IN R3, %TEXT
SUB R3, R3, 0x30
BRL .HexInSkip, R3, 0xA
SUB R3, R3, 7
.HexInSkip
BSL R3, R3, R4
OR R2, R2, R3
BNZ .Loop4, R4
POP R3
STR R3, R2
JMP .Loop3

.OutHex
AND R2, R4, 0xF000 // R2 = op1
BSR R2, R2, 12
ADD R2, R2, M35
LOD R3, R2
AND R3, R3, 0xF000
BSR R3, R3, 12
ADD R3, R3, 0x30
BRL .HexOut1, R3, 0x3A
ADD R3, R3, 7
.HexOut1
OUT %TEXT, R3
AND R3, R2, 0x0F00
AND R4, R2, 0x00F0
AND R2, R2, 0x000F
BSR R3, R3, 8
BSR R4, R4, 4
ADD R3, R3, 0x30
BRL .HexOut2, R3, 0x3A
ADD R3, R3, 7
.HexOut2
OUT %TEXT, R3
ADD R4, R4, 0x30
BRL .HexOut3, R4, 0x3A
ADD R4, R4, 7
.HexOut3
OUT %TEXT, R4
ADD R2, R2, 0x30
BRL .HexOut4, R2, 0x3A
ADD R2, R2, 7
.HexOut4
OUT %TEXT, R2
JMP .Loop3

.ChangeWorkingDirectory
CAL .Get1op
STR M52, R3 //.CurrentWorkingDirectory
JMP .Loop3

.ChangeWorkingDirectoryFromCurrent
CAL .Get1op
BRE .CDFCroot, R3, 0xFFFF
BSL R3, R3, 2 //mult by 4
ADD R3, R3, 3
LOD R4, M52 //.CurrentWorkingDirectory load current working directory
ADD R3, R3, R4 //add offset
LOD R3, R3 //load address
STR M52, R3 //.CurrentWorkingDirectory
JMP .Loop3
.CDFCroot
LOD R3, M53 //.RootDirectory
STR M52, M76 //.CurrentWorkingDirectory .Files
JMP .Loop3

//* EXTRA FUNCTIONS *//
.Error
OUT %TEXT, 0x45 // 'E'
OUT %TEXT, 0x72 // 'r'
OUT %TEXT, 0x72 // 'r'
OUT %TEXT, 0x6F // 'o'
OUT %TEXT, 0x72 // 'r'
OUT %TEXT, 0xA // '\n'
JMP .ShellEnd

.Get2op
AND R2, R4, 0xF000 // R2 = op1
AND R3, R4, 0x0F00 // R3 = op2
BSR R2, R2, 12
BSR R3, R3, 8
ADD R2, M35, R2 // Add offset
ADD R3, M35, R3
LOD R4, R2
LOD R3, R3 // Load values
RET

.Get1op
AND R2, R4, 0xF000 // R2 = op1
BSR R2, R2, 12
ADD R2, R2, M35
LOD R3, R2
RET

.Backspace
POP R1
JMP .ShellLoop