diff --git a/Software_Assembly/ek-konis/ek.konis.txt b/Software_Assembly/ek-konis/ek.konis.txt new file mode 100644 index 0000000..7a9d2a6 --- /dev/null +++ b/Software_Assembly/ek-konis/ek.konis.txt @@ -0,0 +1,214 @@ +EK-KONIS +========= +"From fine dust we assemble" + +A collection of libraries targeting the ICMC Processor. +See: https://github.com/simoesusp/Processador-ICMC + +These libraries are student-made and provided as-is. They are +intended to make common tasks easier without hiding what is +happening under the hood. Read the individual manuals before +using a library in your own code. + +For more Up to date libs (since pull requests might take long to be aproved): +See: https://github.com/NomeGenerico/ek-konis + +================================================================ +QUICK START +================================================================ + +1. Place your .asm file in the root folder. +2. Declare your includes at the top of your file: + + ;#Include Control.asm + ;#Include String.asm + +3. Run the linker: + + python linker.py input.asm output.asm + +4. Assemble output.asm as usual. + +Include order does not matter. The linker resolves dependencies +automatically. See manuels/linker.txt for full details. + +RUNNING LIBRARY TESTS +---------------------- +Each library file contains tests below the ;END OF LIB marker. +Since the linker strips everything outside START/END markers when +resolving dependencies, these tests only run when the library is +passed directly as the input file. + +To run the tests for a library: + + python linker.py libs/String.asm out.asm + +Then assemble and run out.asm as usual. The tests are stripped +automatically when the library is included by another file, so +they will never appear in your own builds. + +================================================================ +TOOLS +================================================================ + + linker.py - Resolves ;#Include directives and stitches + libraries into a single output file. + Requires Python 3. + See manuels/linker.txt for full details. + + stringf.py - (Work in progress) A QOL formatter for FStr + string declarations. Intended to simplify + writing FStr data without editing the assembler. + Not fully functional yet. + + +================================================================ +LIBRARIES +================================================================ + +Libraries are numbered in recommended include order. A library +always depends on the ones before it, never the ones after. + +---------------------------------------------------------------- +0 — CONTROL (Control.asm) +---------------------------------------------------------------- +Extends the instruction set with indirect calls. Small but +foundational — several other libraries depend on it. + + Provides: + CallI - Call a function whose address is in a register + + Manual: manuels/control.txt + + +---------------------------------------------------------------- +1 — STRING (String.asm) +---------------------------------------------------------------- +String printing utilities with support for formatted strings +(FStr). FStr allows embedding numbers and nested strings +directly into string data, similar to printf in C. + + Provides: + PrintStr - Print a plain string + PrintFStr - Print a formatted string + PrintHexNumberOnScreen - Print a number in hex + PrintDecNumOnScreen - Print a number in decimal + + Format specifiers (inside FStr strings): + 256 - Embed a hex number + 257 - Embed a decimal number + 258 - Embed a nested string + + Easily extendable with new specifiers. See the manual. + + Requires: Control.asm + Manual: manuels/string.txt + + +---------------------------------------------------------------- +2 — ERROR HANDLER (ErrorHandler.asm) +---------------------------------------------------------------- +Minimalist error handling. Triggers a Yellow Screen of Death +(YSOD) on fatal errors, prints a custom error message, and +attempts a stack trace to show what was called before the crash. + + Provides: + CallFatalError - Halt with a YSOD and message + CheckIfZero - Assert a value is zero + CheckIfOne - Assert a value is one + CheckOverFlow - Assert a write is within bounds + CheckOverFlowSafe - Same, but returns instead of halting + MemCompare - Compare two memory regions + ErrorAwareCall - Call a function, traceable on error + + Define your own error messages and assign them IDs. + Functions called via ErrorAwareCall will appear in the + stack trace. See the manual for setup instructions. + + Requires: String.asm + Manual: manuels/ErrorHandler.txt + + +---------------------------------------------------------------- +3 — RLE COMPRESSION (RLE.asm) +---------------------------------------------------------------- +Run-Length Encoding compression. Effective on data with long +runs of repeated values. Depending on the data structure, +compression of up to 88% is achievable. + + Provides: + RLEEncoder - Compress data into RLE format + RLEDecoder - Decompress RLE data into flat memory + RLETraverser - Read a single value by index without + fully decompressing, with caching for + fast sequential access + + Not yet implemented: + RLEPartialDecoder - Decode a subrange of RLE data + RLERectangleDecoder - Decode RLE data with a 2D stride + + Requires: String.asm + Manual: manuels/RLE.txt + + +================================================================ +IN ACTIVE DEVELOPMENT +================================================================ + +These libraries are not yet ready for use. Descriptions are +provided for reference. + +---------------------------------------------------------------- +4 — MEMORY HANDLER (MemoryHandler.asm) +---------------------------------------------------------------- +Dynamic memory allocation. Essentially malloc and free for the +ICMC processor. Allows declaring objects in memory at runtime +with minimal overhead. Few guardrails, but useful. + + +---------------------------------------------------------------- +5 — DIRTY RECTANGLE RENDERING (DirtyRectangle.asm) +---------------------------------------------------------------- +Optimized screen rendering that only redraws what has changed +since the last frame. Features include: + + - Multiple layers with z-ordering + - Default colors per layer + - Custom colors per screen index + + +---------------------------------------------------------------- +6 — UI SYSTEM (UiSystem.asm) +---------------------------------------------------------------- +Interactable menus with selection, confirmation, and highlight +support. To use: + + 1. Provide an RLE-compressed string of the appearance + 2. Define selectable regions + 3. Write a handler function for each region + 4. It just works + +Default limit of 20 stacked elements, configurable. + + +---------------------------------------------------------------- +7 — OBJECT SYSTEM (ObjectSystem.asm) +---------------------------------------------------------------- +A basic object model for the ICMC processor: + + - Create objects (constructors are still yours to write) + - Dispatch behavior functions by object type + - Store and access custom data per object via an Object ID + + +================================================================ +MANUALS +================================================================ + +Individual documentation files are in the libs/manuels/ folder: + + control.txt - Control library + string.txt - String library + ErrorHandler.txt - Error handler library + RLE.txt - RLE compression library + linker.txt - Linker usage and conventions diff --git a/Software_Assembly/ek-konis/libs/Control.asm b/Software_Assembly/ek-konis/libs/Control.asm new file mode 100644 index 0000000..816c729 --- /dev/null +++ b/Software_Assembly/ek-konis/libs/Control.asm @@ -0,0 +1,11 @@ +; Library to help with control flow of instructions. Adds some usefull stuff +; +; +; + +;START OF LIB +CallI: + push r7 + rts +;END OF LIB + diff --git a/Software_Assembly/ek-konis/libs/ErrorHandler.asm b/Software_Assembly/ek-konis/libs/ErrorHandler.asm new file mode 100644 index 0000000..44bada0 --- /dev/null +++ b/Software_Assembly/ek-konis/libs/ErrorHandler.asm @@ -0,0 +1,732 @@ +loadn r0, #65534 +push r0 ; stop stack overflow +jmp main + +;#Include String.asm + +;START OF LIB + +;--------- ErrorSystem Version 0.3.0 +; +; Minimalist Suit To Allow For Easier Error Catching: +; Is required in all my libraries to allow you to know imediatly +; if the error is related to my shitty code or your shity code +; +; Error Mesages and ErrorMessageTables can be moved at will, but they must exist somewhere +; +; The Usage Is prety simple, Define Error Mesages and Assing them to IDs. Call "CallFatalError" with the IDs to get a +; Yellow Screen Of Death (YSOD) +; +; Some Other Functions may also provided, They are mainly used for my other libraries but are general error cheking that you can use. +; +; Please Respect the Private And Public Declarations. You are king, but Private Functions May Use Undeclared Conventions +; That might make Debuging Hell +; +; All my functions have input and output documented like this , +; If there are no resgister indication you can just assume the first is r0, the second r1, etc +; + + + ; RoadMap For V 0.4.0 + ; - Improve the Stack Trace + + + + + ;Private + PrintYellowScreen: ; < > , < > + + push r0 + push r1 + push r2 + + loadn r0, #0 + loadn r1, #1200 + loadn r2, #893 ; YellowSquare + + PrintYellowScreen_loop: + + + cmp r0, r1 + jeq PrintYellowScreen_Exit + + outchar r2, r0 + inc r0 + jmp PrintYellowScreen_loop + PrintYellowScreen_Exit: + pop r2 + pop r1 + pop r0 + rts + + + PrintErrorMessage: ; , + + ;r0 printing position + push r1 ; String Address ; its now a pointer + push r2 ; color + push r3 + push r4 + push r5 + + loadn r5, #0 ; stops garbage + + + loadn r1, #ErrorMessageTable + add r1, r1, r0 ; Use Id to find string + loadi r1, r1 ; now its the string addres + + loadn r0, #0 ; load the position to write to + loadn r2, #0 + + call PrintFStr + + pop r5 + pop r4 ; Resgata os valores dos registradores utilizados na Subrotina da Pilha + pop r3 + pop r2 + pop r1 + rts + + + + TraceStack: ; Only gets called on Fatal Error + + pop r6 + loadn r3, #65535 + loadn r5, #65534 + + TraceStack_loop: + + + ; if r2 = 65535, add word to to TraceBuffer + + pop r2 + cmp r2, r3 + jne TraceStack_skipadd + + ;pop r2 ; addres after the sentinel. Its actualy the callers r1 + pop r2 ; Actual return adrres + call TraceBufferAdd ; adds r2 to list + + TraceStack_skipadd: + + + ; if r2 = 65534, exit beacuse we reached the end of the stack + cmp r2, r5 + jeq TraceStack_exit + + jmp TraceStack_loop + TraceStack_exit: + push r6 + rts + + + + ; Public: + CallFatalError: ; , < > ; + + + call PrintYellowScreen + call PrintErrorMessage ; Puts + + ; retrieve PC from stack + + pop r1 + + ; try to find stack marker + + call TraceStack + + call TraceBufferPrint + ; Print PC after the message + + halt + + CheckOverFlowSafe: ; < , Size, Buffer* , BufferSize, BufferWritePointer >, < r2 = Error or not> Used By the Memory Handler + + push r1 ; Size of alocated object + push r2 ; Buffer* (Start of buffer) + push r3 ; BufferSize + push r4 ; BufferWritePointer + + ;r1 = Size of Object + + add r1, r1, r4 ; BufferWritePointer + Size + add r2, r2, r3 ; Buffer* + BufferSize + + cmp r1, r2 + jel CheckOverFlowSafe_END + + loadn r2, #1 + + pop r4 + pop r3 + pop r1 + + + rts + + CheckOverFlowSafe_END: + + loadn r2, #0 + + pop r4 + pop r3 + pop r2 + pop r1 + + + rts + + CheckOverFlow: ; < ErrorID , Size, Buffer*, BufferSize, BufferWritePointer > , < > + + push r1 ; Size of alocated object + push r2 ; Buffer* (Start of buffer) + ; r3 ; BufferSize + ; r4 ; BufferWritePointer + + Call CheckOverFlowSafe + + mov r1, r2 + call CheckIfZero ; Errors if not zero + + + pop r2 + pop r1 + + rts + + MemCompare: ; <*mem1 , *mem2, size> , < Equal (0 or 1)> , 1 if equal, 0 if different ; safe + + push r1 + push r2 + push r3 + push r4 + push r5 + + loadn r5, #0 + + MemCompare_loop: + + loadi r3, r0 + loadi r4, r1 + cmp r3, r4 + jeq MemCompare_equal + + loadn r0, #0 + jmp MemCompare_exit + + MemCompare_equal: + + inc r0 + inc r1 + dec r2 + cmp r2, r5 + jne MemCompare_loop + loadn r0, #1 + MemCompare_exit: + + pop r5 + pop r4 + pop r3 + pop r2 + pop r1 + + rts + CheckIfOne: ; , < > + push r1 + push r2 + + loadn r2, #1 + cmp r1, r2 + jne CheckIfOne_error + + pop r2 + pop r1 + rts + + CheckIfOne_error: + + pop r2 + pop r1 + call CallFatalError + + + CheckIfZero: ; , < > + push r1 + push r2 + + loadn r2, #0 + cmp r1, r2 + jne CheckIfZero_error + + pop r2 + pop r1 + rts + + CheckIfZero_error: + + pop r2 + pop r1 + call CallFatalError + ;Untested + ErrorAwareCall: ; < ErrorID, r7 = Which Function to Call (Pointer) > Clobers r1!!! SAVE IT before calling + + loadn r1, #65535 ; marker for call, to guarantee to find the original functions addres + push r1 + + call CallI ; will call whatver is in r7 + + pop r1 + rts + + +;----------- StackTracer +; +; For a stack trace that does not have unexpected behaviour we can do either a ring buffer or a just exit when the buffer gets filled +; +; Lets Exit When Buffer Gets Filled, mostlikely, we will want calls that happenned later +; + + TraceBuffer: var #10 ; Can store up to 10 Traces + TraceBufferSize: var #1 + static TraceBufferSize + #0, #10 + TraceBufferPointer: var #1 + static TraceBufferPointer + #0, #TraceBuffer + + + TraceBufferAdd: ; < r2 = Data>, + + push r0 + push r1 + push r2 + + mov r0, r2 + + ; check if buffer is full + + loadn r1, #TraceBuffer + load r2, TraceBufferSize + add r1, r1, r2 + + + load r2, TraceBufferPointer + + cmp r1, r2 + jne TraceBufferAdd_Continue + + ; code executed if buffer out of space + + loadn r0, #1 + + pop r2 + pop r1 + pop r0 + rts + + TraceBufferAdd_Continue: + + storei r2, r0 ; store Data in location pointed by TraceBufferPointer + loadn r0, #0 + + inc r2 + store TraceBufferPointer, r2 + + pop r2 + pop r1 + pop r0 + rts + + + + + TraceBufferPrint: ; < r0 = index> + + loadn r2, #TraceBuffer + load r3, TraceBufferPointer + loadn r4, #35 ; the PrintHex Alredy increments 5 + + + TraceBufferPrint_loop: + + cmp r2, r3 ; compare with end of buffer + jeg TraceBufferPrint_exit + + loadi r1, r2 + add r0, r0, r4 + + call PrintHexNumberOnScreen + + inc r2 + + jmp TraceBufferPrint_loop + TraceBufferPrint_exit: + + rts + + +;END OF LIB + +;----------- Error Messages ; Examples Used for the tests +; +; + ; Error Mesages + ; + ; Simply declare a String and add it to the Error MessageTable. The value in the + #num will be the Error ID. + ; + TestError : string "This is a Error Mesage, PC that called the error: " + BufferOverFlowError1 : string "The OverFlow Function Failed: Test 1. PC:" + BufferOverFlowError2 : string "The OverFlow Function Failed: Test 2. PC:" + BufferOverFlowError3 : string "The OverFlow Function Failed: Test 3. PC:" + BufferOverFlowError4 : string "The OverFlow Function Failed: Test 4. PC:" + BufferOverFlowError5 : string "The OverFlow Function Failed: Test 5. PC:" + MemCompareError1 : string "The MEMCompare Function Failed Test 1. PC:" + MemCompareError2 : string "The MEMCompare Function Failed Test 2. PC:" + MemCompareError3 : string "The MEMCompare Function Failed Test 3. PC:" + MemCompareError4 : string "The MEMCompare Function Failed Test 4. PC:" + MemCompareError5 : string "The MEMCompare Function Failed Test 5. PC:" + ErrorAwareCallError1 : string "All Previous Tests Passed! This Tests Can only run once, make sure to check the stack trace to make sure it works, the rest is OK!:" + + + + ErrorMessageTable: var #256 + + static ErrorMessageTable + #0, #TestError + static ErrorMessageTable + #10, #BufferOverFlowError1 + static ErrorMessageTable + #11, #BufferOverFlowError2 + static ErrorMessageTable + #12, #BufferOverFlowError3 + static ErrorMessageTable + #13, #BufferOverFlowError4 + static ErrorMessageTable + #14, #BufferOverFlowError5 + static ErrorMessageTable + #15, #MemCompareError1 + static ErrorMessageTable + #16, #MemCompareError2 + static ErrorMessageTable + #17, #MemCompareError3 + static ErrorMessageTable + #18, #MemCompareError4 + static ErrorMessageTable + #19, #MemCompareError5 + static ErrorMessageTable + #20, #ErrorAwareCallError1 + + + +; DO NOT COPY TO YOUR CODE, ALSO REMOVE THE JUMP MAIN ON THE TOP +; Library Tests +; +; + ;DO NOT COPy + ;DO NOT COPY + ; Inverted Functions, Will Trow Errors if passed, Pass If Failed. + CheckOverFlowReversed: ; Used By the Memory Handler + + push r1 + push r2 + push r3 + push r4 + + ;r0 = ERRORID + ;r1 = Size of Object + + add r1, r1, r4 ; Pointer + Size + add r2, r2, r3 ; Buffer + BufferSize + + cmp r1, r2 + jel CheckOverFlowReversed_END + + pop r4 + pop r3 + pop r2 + pop r1 + + rts + + + CheckOverFlowReversed_END: + + Call CallFatalError + + pop r4 + pop r3 + pop r2 + pop r1 + + rts + ;DO NOT COPY + ; Cleanup Functions + CleanScreen: + + loadn r0, #0 ; void + loadn r1, #0 + loadn r2, #1200 + + CleanScreen_loop: + cmp r1, r2 + jeq CleanScreen_Exit + outchar r0, r1 + inc r1 + jmp CleanScreen_loop + CleanScreen_Exit: + + rts + + + + + ;DO NOT COPY + ; Actual Test Code + ; + ; + ; TEST DATA + + TestBuffer: var #2000 + TestBufferSize: var #1 + static TestBufferSize + #0, #2000 + TestBufferPointer: var #1 ; this point to the furthest WRITTEN data along the list. + static TestBufferPointer + #0, #TestBuffer + + + + ;DO NOT COPY + main: + call CleanScreen + ; BufferOverFlow TESTS: + + loadn r0, #1900 + load r1, TestBufferPointer + add r1, r1, r0 + store TestBufferPointer, r1 + + ; Test1 ; Alocated Up to Boundry + loadn r0, #10 ; Error ID + loadn r1, #100 ; size of alocation + loadn r2, #TestBuffer + load r3, TestBufferSize + load r4, TestBufferPointer + + call CheckOverFlow ; expected pass + + ; Test2 - Clear overflow, should error + loadn r0, #11 + loadn r1, #200 ; size too big + loadn r2, #TestBuffer + load r3, TestBufferSize + load r4, TestBufferPointer ; still at 1900 + call CheckOverFlowReversed ; expected error so we use reverse, to allow tests to continue + + ; Test3 - Pointer at start, small allocation, should pass + loadn r0, #12 + loadn r1, #10 + loadn r2, #TestBuffer + load r3, TestBufferSize + loadn r4, #TestBuffer ; pointer at start + call CheckOverFlow ; expected pass + + ; Test4 - Pointer at start, allocation larger than buffer, should error + loadn r0, #13 + loadn r1, #2001 + loadn r2, #TestBuffer + load r3, TestBufferSize + loadn r4, #TestBuffer + call CheckOverFlowReversed ; expected error so we use reverse, to allow tests to continue + + ; Test5 - Pointer already past buffer end, should error + loadn r0, #14 + loadn r1, #1 ; alocate a single word + loadn r2, #TestBuffer + load r3, TestBufferSize + load r4, TestBufferPointer ; still at 1900 range + loadn r5, #300 + add r4, r4, r5 ; Buffer Pointer Is now Buffer + 2200 + call CheckOverFlowReversed ; expected error so we use reverse, to allow tests to continue + + ; MEMCompare Tests: + ; setup + TestMem1 : var #5 + static TestMem1 + #0 , #5 + static TestMem1 + #1 , #7 + static TestMem1 + #2 , #3 + static TestMem1 + #3 , #2 + static TestMem1 + #4 , #1 + TestMem1Over : var #1 + static TestMem1Over + #0 , #7 + + TestMem2 : var #5 ; different from Mem1 only in overflow + static TestMem2 + #0 , #5 + static TestMem2 + #1 , #7 + static TestMem2 + #2 , #3 + static TestMem2 + #3 , #2 + static TestMem2 + #4 , #1 + TestMem2Over : var #1 + static TestMem2Over + #0 , #9 + + TestMem3 : var #5 ; Different from Mem1 Only in last valid addrs + static TestMem3 + #0 , #5 + static TestMem3 + #1 , #7 + static TestMem3 + #2 , #3 + static TestMem3 + #3 , #2 + static TestMem3 + #4 , #6 + TestMem3Over : var #1 + static TestMem3Over + #0 , #7 + + TestMem4 : var #5 ; Different from Mem1 Only in start + static TestMem4 + #0 , #2 + static TestMem4 + #1 , #7 + static TestMem4 + #2 , #3 + static TestMem4 + #3 , #2 + static TestMem4 + #4 , #1 + TestMem4Over : var #1 + static TestMem4Over + #0 , #7 + + TestMem5 : var #5 ; Different drom Mem1 only in the middle + static TestMem5 + #0 , #5 + static TestMem5 + #1 , #7 + static TestMem5 + #2 , #2 + static TestMem5 + #3 , #2 + static TestMem5 + #4 , #1 + TestMem5Over : var #1 + static TestMem5Over + #0 , #7 + + ; Test 1 + loadn r0, #TestMem1 + loadn r1, #TestMem1 + loadn r2, #5 ; Size + call MemCompare + ; r0 is now 0, 1 + mov r1, r0 + loadn r0, #15 ; should give 1 (equal) + call CheckIfOne + + ; Test 2 + loadn r0, #TestMem1 + loadn r1, #TestMem2 + loadn r2, #5 ; Size + call MemCompare + ; r0 is now 0, 1 + mov r1, r0 + loadn r0, #16 ; should give 1 (equal) + call CheckIfOne + + ; Test 3 + loadn r0, #TestMem1 + loadn r1, #TestMem3 + loadn r2, #5 ; Size + call MemCompare + ; r0 is now 0, 1 + mov r1, r0 + loadn r0, #17 ; should give 0 (diff) + call CheckIfZero + + ; Test 4 + loadn r0, #TestMem1 + loadn r1, #TestMem4 + loadn r2, #5 ; Size + call MemCompare + ; r0 is now 0, 1 + mov r1, r0 + loadn r0, #18 ; should give 0 (diff) + call CheckIfZero + + ; Test 5 + loadn r0, #TestMem1 + loadn r1, #TestMem5 + loadn r2, #5 ; Size + call MemCompare + ; r0 is now 0, 1 + mov r1, r0 + loadn r0, #19 ; should give 0 (diff) + call CheckIfZero + + + + ; StackTraceTests + + ; SetUp + jmp skipTraceSetUp + TestFunction1: + + rts + + TestFunction2: + + call TestFunction1 + + rts + + TestFunction3: + + loadn r7, #TestFunction4 + call ErrorAwareCall + + rts + + TestFunction4: + + loadn r7, #TestFunction5 + call ErrorAwareCall + + rts + + TestFunction5: + + call CallFatalError + + rts + + + + TestBigFunction1:; + + loadn r7, #TestFunction1 + call ErrorAwareCall + call TestFunction2 + call TestFunction2 + call TestFunction1 + loadn r7, #TestFunction2 ; Puts one Error AwareCall, Returns and removes the sentinel + call ErrorAwareCall + call TestFunction1 + + rts + + TestBigFunction2:; + + loadn r7, #TestFunction1 + call ErrorAwareCall + call TestFunction2 + call TestFunction2 + call TestFunction1 + loadn r7, #TestFunction2 + call ErrorAwareCall + call TestFunction1 + call TestFunction3 + + rts + + skipTraceSetUp: + + ; test 1 + loadn r0, #20 + loadn r1, #10 + loadn r2, #34 + + push r1 + push r2 + push r1 + push r2 + + push r1 ; needed because TestBigFunction Clobers r1 + loadn r7, #TestBigFunction1 + call ErrorAwareCall + pop r1 + + push r1 + push r2 + push r2 + push r1 + + push r1 + loadn r7, #TestBigFunction2 ; will trow an error, + call ErrorAwareCall + pop r1 + + halt + ; All Tests Passed + + + diff --git a/Software_Assembly/ek-konis/libs/RLE.asm b/Software_Assembly/ek-konis/libs/RLE.asm new file mode 100644 index 0000000..65bd542 --- /dev/null +++ b/Software_Assembly/ek-konis/libs/RLE.asm @@ -0,0 +1,424 @@ +loadn r0, #65534 +push r0 ; stop stack overflow in StackTrace of the error handler +jmp Tests + +;#Include ErrorHandler.asm +;#Include String.asm + +;START OF LIB +;-------------- RLE Library V0.1 +; +; RLE Stands for Run Lenght Enocding. It is a simple compression method that is incredibly simple, but very efective at +; some kinds of repetitive sequential data. Strings like "00000000100000111111111" which would take 23 words in memory, would take [8,"0",1,"1",5,"0",9,"1"], which +; is just 8 words. More than 50% compression. It works by storing a count and a object pair, for each element it finds. But if repeated and sequential elements are found, +; it only needs to increment the count, which does not use more memory. +; + ;Private + + ;Public + ; Fully Working + RLEDecoder: ; <*target, *source>, <> ; Decodes the data in r1 to the memory that starts in r0 + push r0 + push r1 + push r2 + push r3 + push r4 + ; r0 is the string it will decode to. Pointer + ; r1 is the string it will decode from. Pointer + + loadn r3, #'\0' + + RLEDecoder_Loop: + loadi r4, r1 ; Carrega no r4 o caractere apontado por r1 + cmp r4, r3 ; Compara o caractere atual com '\0' + jeq RLEDecoder_Exit ; Se for igual a '\0', salta para ImprimeStr_Sai, encerrando a impressão. + + + mov r2, r4 ; loop lengh + inc r1 + loadi r4, r1 ; looped character + inc r2 ; makes loop easier, no need to compare to zero + CharacterDecode_Loop: + dec r2 + jz CharacterDecode_Exit + + storei r0, r4 + inc r0 + + jmp CharacterDecode_Loop + + CharacterDecode_Exit: + inc r1 + jmp RLEDecoder_Loop ; Volta ao início do loop para continuar imprimindo. + + RLEDecoder_Exit: + pop r4 ; Resgata os valores dos registradores utilizados na Subrotina da Pilha + pop r3 + pop r2 + pop r1 + pop r0 + rts + + + + RLEEncoder: ; <*target, *source, size> | < r2 = SizeOfCompressedData > ; Encodes data from source to target.; Be carefull, output data here does not have a preditable output lenght + + push r0 + push r1 + ; r2 is output + push r3 + push r4 + push r5 + push r6 + push r7 + + loadn r7, #0 + + mov r3, r2 + loadn r2, #0 ; char counter + loadi r4, r1 ; preivous char + loadn r6, #0 ; permanent counter + + RLEEncoder_loop: + + cmp r3, r6 ; size / chars read + jel RLEEncoder_exit + + loadi r5, r1 ; char in source + + cmp r5, r4 ; compare current char with prev char + jeq RLEEncoder_CharEqual + ; not equal + storei r0, r2 ; store counter + inc r0 + inc r7 + storei r0, r4 ; store previous char + inc r0 ; *count of new char + inc r7 + mov r4, r5 ; new char being counted + loadn r2, #0; re-start count + + RLEEncoder_CharEqual: + inc r2 ; count of char + inc r1 ; *source + inc r6 ; permanent counter + + jmp RLEEncoder_loop + + RLEEncoder_exit: + + storei r0, r2 ; store counter + inc r0 + inc r7 + storei r0, r4 ; store previous char + inc r0 + inc r7 + push r7 + loadn r7, #0 + storei r0, r7 ; count of 0 = terminator + pop r7 + inc r7 + mov r2, r7 + + pop r7 + pop r6 + pop r5 + pop r4 + pop r3 + ; r2 = Size Of Data + pop r1 + pop r0 + + rts + + RLETraverser: ; , + + push r0 + push r1 + push r2 + push r4 + push r5 + push r6 + + ; What does the buffer coitain. A index in the RLE string and A Target index. What it tells us is + ; adding up all counts up to where we are in the RLE String is the Target Index Stored. As such we can know + ; somewhat where we are in the uncompressed RLE string. This speeds up massively reads that are sequential in nature without having to decompress data. + ; And with less overhead. + + ; Compared with a full decode, there is no need to the decode the string, and it uses less memory + ; Compared With a Sequential read of the RLE, it speeds up reading things that are close to each other. + + ; Buffer Layout {Index where the fragment starts, Shift to the count of that fragment itself} + ; so if: Buffered Index <= target < Buffered Index + RLE[Buffered shift], we know that the data is in this fragment, + ; so we inc the RLE pointer, deference it output it. + ; Then we buffer this place again. + + ; IF Buffered Index + RLE[Buffered shift] <= target + ; we inc the pointer 2 times and reepeat what we had done the top again with the new values. + + ; IF target < Buffered Index, we need to go backwards. + + ; get buffered data + loadi r3, r2 ; Buffered Target Index + inc r2 + loadi r4, r2 ; Buffered RLE index + add r4, r1, r4 ; gets beffered position in RLE + dec r2 + + cmp r0, r3 ; the target index is ___ than the buffered target index + + jeq RLETraverser_CasheHit + + jle RLETraverser_Backwards ; If target < Buffer we jump to the backwards loop + + ; code execute in the foward pass + + RLETraverser_FowardsLoop: + loadi r5, r4 ; gets count of current fragment + add r3, r3, r5 ; finds the start of next frag and buffers it + cmp r0, r3 ; is target index < startof next frag (buffered r3) + ; if yes, exit the loop + ; else continue + jle RLETraverser_Exit + inc r4 + inc r4 + jmp RLETraverser_FowardsLoop + + RLETraverser_Backwards: ; We will reach here if the data is inside the current cashed reagion or prior to it. + + dec r4 + dec r4 + + loadi r5, r4 ; gets count of prev fragment + sub r3, r3, r5 ; finds the start of prev frag and buffers it + cmp r0, r3 ; is target index >= startof prev frag (buffered r3) + ; if yes, exit the loop + ; else continue + jeg RLETraverser_CasheHit ; jump greater or equal, we do now want to sub r5 again + jmp RLETraverser_Backwards + + + + RLETraverser_Exit: + sub r3, r3, r5 + RLETraverser_CasheHit: + storei r2, r3 ; store start of next frag + inc r2 + + sub r1, r4, r1 ; get rle index to be buffered + storei r2, r1 + + inc r4 + loadi r3, r4 ; get value at correct target + ; and place it at r3 (exit register) + + pop r6 + pop r5 + pop r4 + pop r2 + pop r1 + pop r0 + rts + + + ; Not Implemented Yet + RLEPartialDecoder: ; , < > ; Could be buffered or not, i'm not sure which is better for now + + ; Takes A RLEstring and decodes from a start index. Can be used to decode a part of interest faster. Could be edited in many ways to suit your code better. + ; You might perfer to pass a index in the RLE instad of the expedcted decoded string, or whathver else you can cook up. + + rts + + RLERectangleDecoder: ; + ;Takes a width and decodes in a special way. Supose you have a list 1200 addresses long, it represents the screen or something paralel to it. + ; You have data that is shaped like a rectangle that is smaller than the screen, now you can save 2 words per unit of height in the encoded data, + ; and probably take less time decoding (Depends on the size, this one takes slightly longer per index) + + rts +;END OF LIB + +; ----- Tests: DO NOT COPY FROM HERE ON +; +; DO NOT COPY DO NOT COPY DO NOT COPY +; + + ; Test DATA ; DATA is arranged like this to make it easier to visualy comapre the memeory to verify results + DecodeTarget: var #100 + TestData1: var #11 ; Rle Compression of 100 long string + static TestData1 + #0, #20 ; first count + static TestData1 + #1, #"A" + static TestData1 + #2, #10 + static TestData1 + #3, #"B" + static TestData1 + #4, #10 + static TestData1 + #5, #"D" + static TestData1 + #6, #30 + static TestData1 + #7, #"E" + static TestData1 + #8, #30 + static TestData1 + #9, #"F" + static TestData1 + #10, #0 + EncodeTarget: var #100 ; we dont know how big the encoded data will be + + TestTraverseBuffer: var #2 + static TestTraverseBuffer + #0, #0 + static TestTraverseBuffer + #1, #0 + + TestReturn: var #2 + static TestReturn + #1, #0 + + + + Tests: + ; Test 1 Encode / Decode + loadn r0, #DecodeTarget + loadn r1, #TestData1 + call RLEDecoder + + loadn r0, #EncodeTarget + loadn r1, #DecodeTarget + loadn r2, #100 + call RLEEncoder + + + loadn r0, #EncodeTarget + loadn r1, #TestData1 + loadn r2, #11 + call MemCompare + mov r1, r0 + loadn r0, #1 + call CheckIfOne + + ; Test2 Traversal + + + loadn r0, #0 + loadn r1, #TestData1 + loadn r2, #TestTraverseBuffer + loadn r4, #"A" ; 0-19 + + call RLETraverser + store TestReturn, r3 + cmp r3, r4 + jeq Skip_Test2Error1 + loadn r0, #2 + call CallFatalError + Skip_Test2Error1: + + loadn r0, #20 + loadn r4, #"B" ; 20 - 29 + call RLETraverser + store TestReturn, r3 + cmp r3, r4 + jeq Skip_Test2Error2 + loadn r0, #3 + call CallFatalError + Skip_Test2Error2: + + loadn r0, #21 + loadn r4, #"B" ; 20 - 29 + call RLETraverser + store TestReturn, r3 + cmp r3, r4 + jeq Skip_Test2Error3 + loadn r0, #4 + call CallFatalError + Skip_Test2Error3: + + loadn r0, #39 + loadn r4, #"D" ; 30 - 39 + call RLETraverser + store TestReturn, r3 + cmp r3, r4 + jeq Skip_Test2Error4 + loadn r0, #4 + call CallFatalError + Skip_Test2Error4: + + loadn r0, #10 + loadn r4, #"A" ; + call RLETraverser + store TestReturn, r3 + cmp r3, r4 + jeq Skip_Test2Error5 + loadn r0, #5 + call CallFatalError + Skip_Test2Error5: + + + loadn r0, #99 + loadn r4, #"F" + call RLETraverser + store TestReturn, r3 + cmp r3, r4 + jeq Skip_Test2Error6 + loadn r0, #6 + call CallFatalError + Skip_Test2Error6: + + + ; all tests Passed + loadn r0, #100 + call CallFatalError + + +; ##### Dependedncies For Tests + +;----------- Error Messages +; +; + ; Error Mesages + ; + ; Simply declare a String and add it to the Error MessageTable. The value in the + #num will be the Error ID. + ; + TestError : string "This is a Error Mesage, PC that called the error: " + EncodeDecodeError1: string "Test 1 failed" + TraverseError1 : string "Traverse Failed Test 1 Buffer: 00 , 00 Returned: 00" + static TraverseError1 + #34, #257 + static TraverseError1 + #35, #TestTraverseBuffer + static TraverseError1 + #39, #257 + static TraverseError1 + #40, TestTraverseBuffer + #1 + static TraverseError1 + #55, #258 + static TraverseError1 + #56, #TestReturn + TraverseError2 : string "Traverse Failed Test 2 Buffer: 00 , 00 Returned: 00" + static TraverseError2 + #34, #257 + static TraverseError2 + #35, #TestTraverseBuffer + static TraverseError2 + #39, #257 + static TraverseError2 + #40, TestTraverseBuffer + #1 + static TraverseError2 + #55, #258 + static TraverseError2 + #56, #TestReturn + TraverseError3 : string "Traverse Failed Test 3 Buffer: 00 , 00 Returned: 00" + static TraverseError3 + #34, #257 + static TraverseError3 + #35, #TestTraverseBuffer + static TraverseError3 + #39, #257 + static TraverseError3 + #40, TestTraverseBuffer + #1 + static TraverseError3 + #55, #258 + static TraverseError3 + #56, #TestReturn + TraverseError4 : string "Traverse Failed Test 4 Buffer: 00 , 00 Returned: 00" + static TraverseError4 + #34, #257 + static TraverseError4 + #35, #TestTraverseBuffer + static TraverseError4 + #39, #257 + static TraverseError4 + #40, TestTraverseBuffer + #1 + static TraverseError4 + #55, #258 + static TraverseError4 + #56, #TestReturn + TraverseError5 : string "Traverse Failed Test 5 Buffer: 00 , 00 Returned: 00 String Location: 00" + static TraverseError5 + #34, #257 + static TraverseError5 + #35, #TestTraverseBuffer + static TraverseError5 + #39, #257 + static TraverseError5 + #40, TestTraverseBuffer + #1 + static TraverseError5 + #56, #257 + static TraverseError5 + #57, #TestReturn + static TraverseError5 + #81, #256 + static TraverseError5 + #82, #TestData1 + + AllTestsPassed : string "All Tests Passed, Nice!" + + + ErrorMessageTable: var #256 + + static ErrorMessageTable + #0, #TestError + static ErrorMessageTable + #1, #EncodeDecodeError1 + static ErrorMessageTable + #2, #TraverseError1 + static ErrorMessageTable + #3, #TraverseError2 + static ErrorMessageTable + #4, #TraverseError3 + static ErrorMessageTable + #5, #TraverseError4 + static ErrorMessageTable + #6, #TraverseError5 + static ErrorMessageTable + #100, #AllTestsPassed diff --git a/Software_Assembly/ek-konis/libs/String.asm b/Software_Assembly/ek-konis/libs/String.asm new file mode 100644 index 0000000..eae6009 --- /dev/null +++ b/Software_Assembly/ek-konis/libs/String.asm @@ -0,0 +1,357 @@ +jmp main + +;#Include Control.asm + +;START OF LIB +; ---------- Basic String Library +; +; Basic Implemenntaion Of a FString. Curently supports Decimal numbers and Hex Numbers. +; +; Dec Will not work with the vlue zero, but i dont want to fix that rn +; +; To make FString experince better we would have to eddit the Assembler. +; + + +;Private + DigitHexFinder: ; , Takes a number from 0 to 15 and gives back the coresponding hex char + push r4 + + loadn r4, #10 + cmp r3, r4 ; Checks if shift is nedded for char + jle DigitHexFinder_SkipShift + loadn r4, #7 + add r3, r3, r4 ; goes from 9 to A + + DigitHexFinder_SkipShift: + loadn r4, #"0" + add r3, r3, r4 ; add num to the char for 0, with the proper handling of 7,8,9 -> A,B,C + pop r4 + rts + + + ;ResolveStringSpecialChar Data + StringSpecialCharNum: var #1 ; this is important to stop many bugs. + ; CSpecial Chars Start at 256 and go up sequencialy. Just define a function to handle them. + static StringSpecialCharNum + #0 , #3 + + StringSpecialCharHandlers: var #3 + static StringSpecialCharHandlers + #0 , #ResolveString_Hex + static StringSpecialCharHandlers + #1 , #ResolveString_Dec + static StringSpecialCharHandlers + #2 , #ResolveString_Str + + ResolveStringSpecialChar: ; assumes r5 = 255 + + push r4 + push r5 + push r6 + push r7 + + sub r4, r4, r5 ; gets a shift that must be added to #StringSpecialCharNum + + load r6, StringSpecialCharNum + cmp r4, r6 + jgr ResolveStringSpecialChar_Exit + + loadn r5, #StringSpecialCharNum + add r5, r5, r4 + + loadi r7, r5 + call CallI + + ResolveStringSpecialChar_Exit: + pop r7 + pop r6 + pop r5 + pop r4 + rts + + ; if all Resolve String functions start looking the same, i can just make a single resolve string that callsI on the Print...OnScreen Functions + ResolveString_Hex: + inc r1 + push r1 + loadi r1, r1 ; gets value in string + loadi r1, r1 ; gets valkue pointed by the ptr + + call PrintHexNumberOnScreen + + dec r0 ; fix the off by one in the str printing + + pop r1 + rts + + ResolveString_Dec: + inc r1 + push r1 + loadi r1, r1 ; gets value in string + loadi r1, r1 ; gets valkue pointed by the ptr + + call PrintDecNumOnScreen + + dec r0 ; fix the off by one in the str printing + + pop r1 + rts + ResolveString_Str: + inc r1 + push r1 + push r2 + + + inc r1 + loadi r2, r1 + dec r1 + + loadi r1, r1 ; gets value in string + + call PrintFStr + + dec r0 ; fix the off by one in the str printing + + pop r2 + pop r1 + rts + + +; Public + PrintHexNumberOnScreen: ; , ; Prints On Screen + + push r2 + push r3 + + ; r0 is the Index to print the number + loadn r3, #"x" + outchar r3, r0 + inc r0 + + loadn r2, #61440 ; Highest Four Bits + and r3, r1, r2 + shiftR0 r3, #12 + call DigitHexFinder + outchar r3, r0 + inc r0 + + loadn r2, #3840 ; High middle Four Bits + and r3, r1, r2 + shiftR0 r3, #8 + call DigitHexFinder + outchar r3, r0 + inc r0 + + loadn r2, #240 ; Low middle Four Bits + and r3, r1, r2 + shiftR0 r3, #4 + call DigitHexFinder + outchar r3, r0 + inc r0 + + loadn r2, #15 ; Low Four Bits + and r3, r1, r2 + call DigitHexFinder + outchar r3, r0 + inc r0 + + pop r3 + pop r2 + + rts + + + PrintDecNumOnScreen_Buffer: var #6 + static PrintDecNumOnScreen_Buffer + #5, #10 ; sentinel + PrintDecNumOnScreen: ; , ; Prints On Screen + + push r1 + push r2 + push r3 + push r4 + push r5 + + PrintDecNumOnScreen_ZeroCheck: + loadn r3, #0 + cmp r1, r3 + jeq PrintDecNumOnScreen_PrintZero + + loadn r2, #PrintDecNumOnScreen_Buffer + loadn r3, #4 + add r2, r2, r3 + loadn r3, #0 + loadn r4, #10 + PrintDecNumOnScreen_DivWhileR1GreaterThanZero: + div r5, r1, r4 ; r5 is the number without the remainder + mul r5, r5, r4 ; r5 is the number with the highest digits + sub r5, r1, r5 ; r5 is the remainder, in this case the least significant digit + storei r2, r5 + dec r2 + div r1, r1, r4 + cmp r1, r3 + jgr PrintDecNumOnScreen_DivWhileR1GreaterThanZero + + loadn r4, #"0" + loadn r5, #9 + PrintDecNumOnScreen_WalkBuffer: + inc r2 + loadi r3, r2 + cmp r3, r5 + jgr PrintDecNumOnScreen_Exit + add r3, r3, r4 + outchar r3, r0 + inc r0 + jmp PrintDecNumOnScreen_WalkBuffer + + ; print zero if the number is zero + + PrintDecNumOnScreen_PrintZero: + loadn r3, #"0" + outchar r3, r0 + inc r0 + + PrintDecNumOnScreen_Exit: + pop r5 + pop r4 + pop r3 + pop r2 + pop r1 + rts + + PrintStr: ; , < > + push r1 ; String Address ; its now a pointer + push r2 ; color + push r3 + push r4 + + loadn r3, #'\0' + + PrintStr_Loop: + loadi r4, r1 ; Carrega no r4 o caractere apontado por r1 + cmp r4, r3 ; Compara o caractere atual com '\0' + jeq PrintStr_Exit ; Se for igual a '\0', salta para ImprimeStr_Exit, encerrando a impressão. + + add r4, r2, r4 ; Soma r2 ao valor do caractere. + + outchar r4, r0 ; Imprime o caractere (r4) na posição de tela (r0). + inc r0 ; Incrementa a posição na tela para o próximo caractere. + inc r1 ; Incrementa o ponteiro da string para o próximo caractere. + jmp PrintStr_Loop ; Volta ao início do loop para continuar imprimindo. + + PrintStr_Exit: + pop r4 ; Resgata os valores dos registradores utilizados na Subrotina da Pilha + pop r3 + pop r2 + pop r1 + rts + + PrintFStr: ; , < Last Printed Pos> a little slower than normal printing + + ; 256 = Hex Num + ; 257 = Dec Num + + ; r0 ; printing position + push r1 ; String Address ; its now a pointer + push r2 ; color + push r3 + push r4 + push r5 + + loadn r3, #'\0' + + loadn r5, #255 + + PrintFStr_Loop: + loadi r4, r1 ; Carrega no r4 o caractere apontado por r1 + cmp r4, r3 ; Compara o caractere atual com '\0' + jeq PrintFStr_Exit ; Se for igual a '\0', salta para ImprimeStr_Exit, encerrando a impressão. + + cmp r4, r5 + jgr PrintFStr_SpecialCharHandler + + ; normal Print + add r4, r2, r4 ; Soma r2 ao valor do caractere. + outchar r4, r0 ; Imprime o caractere (r4) na posição de tela (r0). + + jmp PrintFStr_SkipResolveStringSpecialChar + PrintFStr_SpecialCharHandler: + call ResolveStringSpecialChar + PrintFStr_SkipResolveStringSpecialChar: + + inc r0 ; Incrementa a posição na tela para o próximo caractere. + inc r1 ; Incrementa o ponteiro da string para o próximo caractere. + jmp PrintFStr_Loop ; Volta ao início do loop para continuar imprimindo. + + PrintFStr_Exit: + pop r5 + pop r4 ; Resgata os valores dos registradores utilizados na Subrotina da Pilha + pop r3 + pop r2 + pop r1 + rts + + +;END OF LIB + + +; TESTS +main: + ; Test 1 + + ; DATA + TestHex1 : var #1 + static TestHex1 + #0 , #8246 + TestStr1 : string "Isso e um numero HEX: 00 Texto Depois do Numero" + static TestStr1 + #22, #256 ; Hex Marker + static TestStr1 + #23, #TestHex1 ; Hex num addr + ; Test + loadn r0, #10 + loadn r1, #TestStr1 + loadn r2, #0 + + call PrintFStr + + ; Test 2 + ; DATA + TestDec1 : var #1 + static TestDec1 + #0 , #8246 + TestStr2 : string "Isso e um numero DEC: 00 Texto Depois do Numero" + static TestStr2 + #22, #257 ; DEC Marker + static TestStr2 + #23, #TestDec1 ; Hex num addr + ; Test + loadn r0, #120 + loadn r1, #TestStr2 + loadn r2, #0 + + call PrintFStr + + ; Test 3 + ; DATA + TestInnerString1 : string "Inner_String" + TestStr3 : string "Isso e uma string: 000 Texto Depois do Numero" + static TestStr3 + #19, #258 ; DEC Marker + static TestStr3 + #20, #TestInnerString1 ; Hex num addr + static TestStr3 + #21, #64512 ; color blue + ; Test + loadn r0, #240 + loadn r1, #TestStr3 + loadn r2, #0 + + call PrintFStr + + + ; Test 4 + ; DATA + ;TestInnerString1 : string "Inner_String" + TestStr4 : string "Isso e uma string Recursiva: 000 Texto Depois do Numero" + static TestStr4 + #28, #258 ; Str Marker + static TestStr4 + #29, #TestStr4 ; Str addr + static TestStr4 + #30, #0 + ; Test + loadn r0, #360 + loadn r1, #TestStr4 + loadn r2, #0 + + call PrintFStr + + halt + + + + diff --git a/Software_Assembly/ek-konis/libs/manuels/ErrorHandler.txt b/Software_Assembly/ek-konis/libs/manuels/ErrorHandler.txt new file mode 100644 index 0000000..867f663 --- /dev/null +++ b/Software_Assembly/ek-konis/libs/manuels/ErrorHandler.txt @@ -0,0 +1,252 @@ +ERROR SYSTEM LIBRARY (error.asm) +=================================== +A minimalist error handling system. Provides a Yellow Screen of Death +(YSOD) for fatal errors, basic safety check utilities, memory +comparison, and a stack tracer to help identify where errors occur. + +Requires: String.asm + +Version: 0.3.0 +Planned: 0.4.0 - Improved stack trace + + +================================================================ +SETUP (required before use) +================================================================ + +The library expects an ErrorMessageTable and the error message +strings to be defined somewhere in your code. The table is an +array of pointers, one per error ID, where each pointer points +to an FStr-compatible string. + + ; Define your error messages + ErrMsg_OutOfBounds : string "OUT OF BOUNDS ERROR" + ErrMsg_BadInput : string "BAD INPUT ERROR" + + ; Define the table, one pointer per ID + ErrorMessageTable: var #2 + static ErrorMessageTable + #0, #ErrMsg_OutOfBounds + static ErrorMessageTable + #1, #ErrMsg_BadInput + +Error IDs are just the index into this table. Pass the ID to +CallFatalError or any of the Check functions. + +The table and messages can be placed anywhere in your code, +but they must exist. If the table is missing the error printer +will jump to garbage memory. + + +STACK TRACE SETUP +----------------- +For the stack tracer to work correctly, the value 65534 must +be pushed onto the stack before any other code runs. This acts +as an end-of-stack sentinel so TraceStack knows when to stop +walking. Without it, TraceStack will read past the bottom of +the stack into garbage memory. + +Place this at the very start of your file or main function: + + main: + loadn r0, #65534 + push r0 + ; rest of your code here + +If you see the stack trace behaving unexpectedly on a fatal +error, a missing sentinel is the first thing to check. + +WARNINGS +-------- +The stack trace is not fully reliable. Data in memory that +happens to contain the sentinel values can confuse the tracer +and produce incorrect or incomplete results. Treat it as a +debugging aid rather than a guaranteed accurate call chain. +Reliability will be improved in future versions. + + +================================================================ +PUBLIC FUNCTIONS +================================================================ + +CallFatalError - Triggers a Yellow Screen of Death +---------------------------------------------------------------- + Signature: CallFatalError -> < > (does not return) + + Parameters: + r0 - Error ID + + Description: + Fills the screen with yellow, prints the error message + for the given ID, attempts a stack trace, prints the + trace, then halts. Does not return. + + Notes: + - For the stack trace to work correctly, functions that + you want to appear in the trace must be called via + ErrorAwareCall instead of a regular call. See + ErrorAwareCall below. + + +CheckOverFlowSafe - Checks if a write would overflow a buffer +---------------------------------------------------------------- + Signature: CheckOverFlowSafe -> + + Parameters: + r1 - Size of the object to write + r2 - Pointer to the start of the buffer + r3 - Size of the buffer in words + r4 - Current write pointer (position inside the buffer) + + Returns: + r2 - 1 if overflow would occur, 0 if safe + + Description: + Checks whether writing an object of the given size at + the current write pointer would exceed the buffer bounds. + Returns a result instead of halting, so the caller can + decide what to do. Use CheckOverFlow when you want an + immediate fatal error instead. + + +CheckOverFlow - Checks for overflow and errors if detected +---------------------------------------------------------------- + Signature: CheckOverFlow -> < > + + Parameters: + r0 - Error ID to report if overflow is detected + r1 - Size of the object to write + r2 - Pointer to the start of the buffer + r3 - Size of the buffer in words + r4 - Current write pointer (position inside the buffer) + + Description: + Same check as CheckOverFlowSafe, but calls CallFatalError + with the given Error ID if an overflow would occur. + Use this when an overflow is always a fatal condition. + + +MemCompare - Compares two memory regions word by word +---------------------------------------------------------------- + Signature: MemCompare -> + + Parameters: + r0 - Pointer to the first memory region + r1 - Pointer to the second memory region + r2 - Number of words to compare + + Returns: + r0 - 1 if both regions are equal, 0 if they differ + + Notes: + - Stops at the first differing word. + - r0 and r1 are preserved after the call. + + +CheckIfZero - Errors if a value is not zero +---------------------------------------------------------------- + Signature: CheckIfZero -> < > + + Parameters: + r0 - Error ID to report if the check fails + r1 - Value to check + + Description: + Calls CallFatalError if r1 is not zero. Does nothing + otherwise. Useful for asserting that a function returned + no error. + + +CheckIfOne - Errors if a value is not one +---------------------------------------------------------------- + Signature: CheckIfOne -> < > + + Parameters: + r0 - Error ID to report if the check fails + r1 - Value to check + + Description: + Calls CallFatalError if r1 is not one. Does nothing + otherwise. Useful for asserting that a function returned + success. + + +ErrorAwareCall - Calls a function and marks it for stack tracing +---------------------------------------------------------------- + Signature: ErrorAwareCall -> < > + + Parameters: + r0 - Error ID (passed through to the called function + if it needs to report an error) + r7 - Pointer to the function to call + + Description: + Calls the function in r7 via CallI, but first pushes a + sentinel value (65535) onto the stack. The stack tracer + looks for these sentinels to reconstruct the call chain + when a fatal error occurs. + + Use this instead of a regular call for any function you + want to appear in the stack trace on error. + + How it works: + Before calling, 65535 is pushed as a marker. When + TraceStack walks the stack after a fatal error, it + identifies these markers and records the return address + that follows each one into the trace buffer. This lets + the YSOD display the chain of calls that led to the error. + + IMPORTANT: + - ErrorAwareCall clobbers r1. Save r1 before calling if + you need it after. + - The stack tracer stops when it finds the value 65534. + This is the expected end-of-stack sentinel. Without it + TraceStack will walk off the bottom of the stack. + + +================================================================ +PRIVATE FUNCTIONS (for reference, not meant to be called directly) +================================================================ + +PrintYellowScreen - Fills the screen with yellow squares +---------------------------------------------------------------- + Signature: PrintYellowScreen < > -> < > + Description: + Writes a yellow square character (893) to all 1200 + screen positions. Called by CallFatalError. + + +PrintErrorMessage - Prints the error message for a given ID +---------------------------------------------------------------- + Signature: PrintErrorMessage -> + Description: + Looks up the error message string in ErrorMessageTable + using the error ID, then calls PrintFStr to print it + starting at screen position 0 with no color. + + +TraceStack - Walks the stack looking for call markers +---------------------------------------------------------------- + Signature: called internally by CallFatalError + Description: + Pops values from the stack looking for the sentinel 65535. + When found, records the following return address into the + trace buffer via TraceBufferAdd. Stops when it finds 65534 + (end-of-stack sentinel). Only meaningful if calls were made + via ErrorAwareCall. + + +TraceBufferAdd - Adds an address to the trace buffer +---------------------------------------------------------------- + Signature: TraceBufferAdd -> + Description: + Appends r2 to the trace buffer. Returns 1 in r0 if the + buffer is full, 0 otherwise. The buffer holds up to 10 + entries. Once full, further entries are silently dropped. + + +TraceBufferPrint - Prints the contents of the trace buffer +---------------------------------------------------------------- + Signature: TraceBufferPrint -> < > + Description: + Prints each address in the trace buffer as a hex number + using PrintHexNumberOnScreen, spaced 35 characters apart + on screen. Called by CallFatalError after TraceStack. \ No newline at end of file diff --git a/Software_Assembly/ek-konis/libs/manuels/RLE.txt b/Software_Assembly/ek-konis/libs/manuels/RLE.txt new file mode 100644 index 0000000..023cd04 --- /dev/null +++ b/Software_Assembly/ek-konis/libs/manuels/RLE.txt @@ -0,0 +1,180 @@ +RLE LIBRARY (rle.asm) +======================= +Implementation of Run-Length Encoding (RLE), a simple compression +method that is very effective on data with long runs of repeated +values. Works by storing [count, value] pairs instead of repeating +the value in memory. + +Requires: String.asm + +Example: the string "AAABBC" (6 words) encodes to [3,'A',2,'B',1,'C'] +(6 words here, but on more repetitive data the savings are significant. +"00000000100000111111111" (23 words) encodes to just 8 words.) + + +RLE ENCODED FORMAT +------------------ +Encoded data is a sequence of [count, value] pairs, terminated +by a count of 0: + + [ count ][ value ][ count ][ value ] ... [ 0 ] + +Example for "AAABBC": + + [ 3 ][ 'A' ][ 2 ][ 'B' ][ 1 ][ 'C' ][ 0 ] + +The terminator is a count word of 0, not a null value word. +This means the encoded data always ends with a single 0 word. + + +================================================================ +PUBLIC FUNCTIONS +================================================================ + +RLEDecoder - Decodes RLE data into a flat memory region +---------------------------------------------------------------- + Signature: RLEDecoder -> < > + + Parameters: + r0 - Pointer to the target (destination) memory region + r1 - Pointer to the RLE encoded source data + + Returns: + nothing (r0 and r1 are preserved) + + Description: + Reads [count, value] pairs from the source and writes + each value count times into the target, advancing the + target pointer as it goes. Stops when it reads a count + of 0 (the terminator). + + Notes: + - The target region must be large enough to hold the + fully decoded data. No bounds checking is performed. + - r0 and r1 are not modified after the call. + + +RLEEncoder - Encodes flat data into RLE format +---------------------------------------------------------------- + Signature: RLEEncoder -> + + Parameters: + r0 - Pointer to the target (destination) memory region + r1 - Pointer to the source data + r2 - Size of the source data in words + + Returns: + r2 - Size of the encoded output in words + + Description: + Reads the source data and writes [count, value] pairs + into the target. Consecutive identical values are grouped + into a single pair. Appends a terminating count of 0 at + the end. + + Notes: + - The output size is not predictable in advance. On + worst-case data (no repeated values) the output will + be roughly twice the input size. Allocate generously. + - r0 and r1 are preserved after the call. + - r2 is overwritten with the output size. + + +RLETraverser - Reads a single value at an index in RLE data +---------------------------------------------------------------- + Signature: RLETraverser -> + + Parameters: + r0 - Target index (into the uncompressed data) + r1 - Pointer to the RLE encoded data + r2 - Pointer to the traverser buffer (see below) + + Returns: + r3 - The value at the target index in the uncompressed data + + Description: + Reads a single value from the RLE data at the given + uncompressed index, without decoding the entire string. + Uses a small buffer to cache its position between calls, + making sequential or near-sequential reads significantly + faster than re-scanning from the start each time. + + This is the right choice when you need random or + sequential access into large RLE data without the memory + cost of fully decoding it. + + How it works: + The buffer stores where the traverser last was in both + the uncompressed index space and the RLE string. On each + call it checks whether the target index falls inside the + cached fragment. If yes it returns immediately (cache hit). + If not it walks forward or backward through [count, value] + pairs until it finds the right fragment, then updates the + buffer. + + This means repeated or nearby reads are cheap. A read far + from the last position is slower, proportional to the + distance in fragments. + + Buffer layout (2 words, must be allocated by the caller): + Buffer[0] - Uncompressed index where the cached fragment starts + Buffer[1] - Offset from r1 to the count word of that fragment + + Buffer initialization: + The buffer must be initialized before the first call. + Set both words to 0 to start traversal from the beginning: + + MyBuffer: var #2 + static MyBuffer + #0, #0 + static MyBuffer + #1, #0 + + Notes: + - The same buffer must be passed on every call for the + same RLE string. Using a fresh buffer on each call + defeats the caching and forces a scan from the start. + - Do not share a buffer between two different RLE strings. + - The buffer is kept external to the function rather than + stored internally so that you can have multiple independent + traversal positions on the same RLE string at the same + time. For example, if you need to read two separate regions + of the same compressed data simultaneously, you can + allocate two buffers and pass each one independently. + If the buffer were internal, the function could only track + one position at a time, and each call would overwrite the + last cached position. + - If you alternate between reading two different RLE strings + using the same buffer, the cached position will be wrong + for whichever string was not read last. This will not + cause a crash, but the traverser will have to walk from + the wrong position to find the correct fragment, losing + the performance benefit. Use one buffer per RLE string + you intend to traverse. + + +================================================================ +NOT YET IMPLEMENTED +================================================================ + +RLEPartialDecoder - Decodes a subrange of RLE data +---------------------------------------------------------------- + Planned signature: -> < > + + Description: + Will decode only a specific range of the uncompressed + data rather than the full string. Useful when only a + region of interest is needed. The exact interface + (whether to accept an uncompressed index or an RLE + offset) is still to be decided. + + +RLERectangleDecoder - Decodes RLE data with a stride +---------------------------------------------------------------- + Planned signature: -> < > + + Description: + Designed for RLE data that represents a 2D region + smaller than a flat buffer (e.g. a rectangle on screen). + Decodes with a stride equal to the given width, saving + 2 words per row in the encoded data compared to a full + flat decode. Slightly slower per index than a normal + decode. \ No newline at end of file diff --git a/Software_Assembly/ek-konis/libs/manuels/String.txt b/Software_Assembly/ek-konis/libs/manuels/String.txt new file mode 100644 index 0000000..5c472db --- /dev/null +++ b/Software_Assembly/ek-konis/libs/manuels/String.txt @@ -0,0 +1,221 @@ +STRING LIBRARY (string.asm) +============================= +Basic string printing utilities. Includes plain string printing, +formatted string printing with support for embedded numbers and +nested strings. + +Requires: Control.asm + +Known Limitations: + - Full FStr support (custom special chars) would require + edits to the Assembler. + + +================================================================ +FSTR FORMAT +================================================================ + +An FStr is a null-terminated string where values greater than 255 +act as format specifiers. Each specifier is followed by one or +more extra words in the string data describing what to print. + +Specifier table: + + 256 (Hex) followed by: [ptr to number] + 257 (Dec) followed by: [ptr to number] + 258 (Str) followed by: [ptr to string][color] + +Example memory layout for an FStr with an embedded hex number: + + [ ... normal chars ... ] + [ 256 ] <- specifier: print as hex + [ ptr to value ] <- address of the number to print + [ ... more chars ] + [ 0 ] <- null terminator + +Example data definition: + + MyNum : var #1 + static MyNum, #8246 + + MyStr : string "Value: 00 done" + static MyStr + #7, #256 ; hex specifier + static MyStr + #8, #MyNum ; address of the variable + + +================================================================ +EXTENDING FSTR WITH NEW SPECIFIERS +================================================================ + +New specifier handlers can be added without modifying any +existing code by appending to the handler table. + + StringSpecialCharNum - Total number of registered handlers. + Currently 3. + + StringSpecialCharHandlers - Array of function pointers, one per + specifier, starting from 256. + +Steps to add a new specifier: + + 1. Increment StringSpecialCharNum: + + static StringSpecialCharNum + #0, #4 ; was 3 + + 2. Expand and append to StringSpecialCharHandlers: + + StringSpecialCharHandlers: var #4 ; was 3 + static StringSpecialCharHandlers + #0, #ResolveString_Hex + static StringSpecialCharHandlers + #1, #ResolveString_Dec + static StringSpecialCharHandlers + #2, #ResolveString_Str + static StringSpecialCharHandlers + #3, #ResolveString_MyNew + + 3. Write the handler following the same pattern as the existing + ones: increment r1 past the specifier, read any extra words + you need from the string, call your print function, then + decrement r0 to cancel the extra inc r0 that PrintFStr + will apply after the handler returns. + +The new specifier value will automatically be 256 + its index +in the table (so the example above would be triggered by 259). + + +================================================================ +PUBLIC FUNCTIONS +================================================================ + +PrintStr - Prints a plain string to the screen +---------------------------------------------------------------- + Signature: PrintStr -> + + Parameters: + r0 - Screen position to start printing at + r1 - Address of the string (null-terminated) + r2 - Color value to apply to each character + + Returns: + r0 - Screen position after the last printed character + + Description: + Walks the string character by character, applying the + color value and printing each one until a null terminator + is found. Does not support format specifiers. Use + PrintFStr if you need those. + + +PrintFStr - Prints a formatted string to the screen +---------------------------------------------------------------- + Signature: PrintFStr -> + + Parameters: + r0 - Screen position to start printing at + r1 - Address of the string (null-terminated) + r2 - Color value to apply to regular characters + + Returns: + r0 - Screen position after the last printed character + + Description: + Works like PrintStr but also handles format specifiers + (values above 255) embedded in the string data. When a + specifier is found, the appropriate handler is called to + print the value in its place. Slightly slower than + PrintStr due to the extra check on each character. + + Notes: + - The color value in r2 only applies to regular characters. + Nested strings (specifier 258) carry their own color + defined in the string data. + - Nested strings via specifier 258 are supported, but + embedding a string inside itself will cause an infinite + loop. + + +PrintHexNumberOnScreen - Prints a 16-bit number in hex format +---------------------------------------------------------------- + Signature: PrintHexNumberOnScreen -> + + Parameters: + r0 - Screen position to start printing at + r1 - The number to print + + Returns: + r0 - Screen position after the last printed character + + Description: + Prints the number as a 4-digit hex value prefixed with + 'x'. Always prints all 4 digits including leading zeros. + r0 is always advanced by exactly 5 after the call. + + Example output for the value 8246: + x2036 + + +PrintDecNumOnScreen - Prints a number in decimal format +---------------------------------------------------------------- + Signature: PrintDecNumOnScreen -> + + Parameters: + r0 - Screen position to start printing at + r1 - The number to print + + Returns: + r0 - Screen position after the last printed character + + Description: + Prints the number in decimal. Extracts digits by + repeatedly dividing by 10, stores them in a small + internal buffer, then walks the buffer to print them + in the correct order. Does not print leading zeros. + + Notes: + - Correctly handles zero. + - Maximum supported value is 65535 (unsigned 16-bit). + + +================================================================ +PRIVATE FUNCTIONS (for reference, not meant to be called directly) +================================================================ + +DigitHexFinder - Converts a number (0-15) to its hex character +---------------------------------------------------------------- + Signature: DigitHexFinder -> + Description: + Takes a value from 0 to 15 and returns the corresponding + ASCII character ('0'-'9' or 'A'-'F'). Used internally + by PrintHexNumberOnScreen. + + +ResolveStringSpecialChar - Dispatches a specifier to its handler +---------------------------------------------------------------- + Signature: called internally by PrintFStr + Description: + Called by PrintFStr when a value above 255 is found. + Looks up the correct handler in StringSpecialCharHandlers + and calls it via CallI. If the specifier value is out of + range, it does nothing and returns. + + +ResolveString_Hex - Handler for specifier 256 +---------------------------------------------------------------- + Signature: called internally by ResolveStringSpecialChar + Description: + Reads the variable address from the next word in the + string, loads the value at that address, and calls + PrintHexNumberOnScreen. + + +ResolveString_Dec - Handler for specifier 257 +---------------------------------------------------------------- + Signature: called internally by ResolveStringSpecialChar + Description: + Same as ResolveString_Hex but calls PrintDecNumOnScreen. + + +ResolveString_Str - Handler for specifier 258 +---------------------------------------------------------------- + Signature: called internally by ResolveStringSpecialChar + Description: + Reads the string address and color from the next two + words in the string data, then calls PrintFStr + recursively. diff --git a/Software_Assembly/ek-konis/libs/manuels/control.txt b/Software_Assembly/ek-konis/libs/manuels/control.txt new file mode 100644 index 0000000..be403f5 --- /dev/null +++ b/Software_Assembly/ek-konis/libs/manuels/control.txt @@ -0,0 +1,55 @@ +CONTROL FLOW LIBRARY +==================== + +A library that extends control flow capabilities by adding useful utilities +on top of standard instructions. + + +CallI - Indirect Call +--------------------- + +Performs an indirect function call by jumping to an address stored in a +register, while still preserving the ability to return to the original caller. + +Usage: + + loadn r7, #FunctionAddress + call CallI + + +How it works +------------ + +Normal call behavior: + + A standard call instruction pushes the return address (current PC) onto + the stack and jumps to the destination. After the called function does its + work and pops its saved registers, RTS pulls the return address off the + stack and puts it back into the PC, resuming execution after the original + call. + + Stack layout during a normal call: + + [ Pushed Registers ] <- Top of Stack + [ Return Address ] + [ Rest of Stack... ] + + +CallI behavior: + + CallI hijacks the RTS mechanism to redirect execution to an address + stored in R7. + + CallI: + push r7 ; Push R7 (the target address) onto the top of the stack + rts ; RTS pops R7 into PC, jumping to the target function + + Stack layout after "call CallI" but before rts: + + [ R7 (target addr) ] <- Top of Stack, RTS pops this -> jumps to target + [ Return Address ] <- Next RTS (inside target function) returns here + [ Rest of Stack... ] + + When the target function eventually executes its own RTS, the return + address is now on top of the stack, so execution correctly returns to + the original caller of CallI. \ No newline at end of file diff --git a/Software_Assembly/ek-konis/libs/manuels/linker.txt b/Software_Assembly/ek-konis/libs/manuels/linker.txt new file mode 100644 index 0000000..5f0ba98 --- /dev/null +++ b/Software_Assembly/ek-konis/libs/manuels/linker.txt @@ -0,0 +1,108 @@ +LINKER (linker.py) +==================== +A simple Python pre-processor that resolves ;#Include directives +in assembly files, stitching together a single output file ready +for the assembler. Handles duplicate includes automatically. + + +USAGE +----- + python linker.py + + Example: + python linker.py main.asm out.asm + + +FOLDER STRUCTURE +---------------- + ROOT/ + linker.py + out.asm <- default output location + + libs/ <- all library .asm files go here + Control.asm + String.asm + RLE.asm + ErrorHandler.asm + MemoryHandler.asm + RingBuffer.asm + + manuels/ <- documentation for each library + control.txt + string.txt + RLE.txt + ErrorHandler.txt + + +HOW IT WORKS +------------ +The linker scans the input file for ;#Include directives and +replaces each one with the contents of the referenced library. +It then rescans the result for further includes, repeating until +no includes remain. This means dependency chains are resolved +automatically regardless of depth. + +For example, if main.asm includes String.asm, and String.asm +includes Control.asm, the output will contain Control.asm first, +then String.asm, then the rest of main.asm. + +Duplicate includes are detected and skipped. If a library has +already been included once, any further ;#Include directives +for that same file are replaced with a comment: + + ; --- SKIP DUPLICATE INCLUDE: String.asm --- + +This means you do not need to manually manage include order or +guard against double inclusion. + + +LIBRARY FILE CONVENTIONS +------------------------- +The linker does not copy entire library files. It only copies +the region between the markers: + + ;START OF LIB + ... library code ... + ;END OF LIB + +Any code outside these markers (tests, scratch code, jmp main, +etc.) is excluded from the output. This lets you keep test code +in the same file as the library without it leaking into builds. + +;#Include directives are always kept regardless of where they +appear in the file, even outside the START/END markers. This +is how dependency chains are declared. + +In the output file, each included library is wrapped in comments +marking where it begins and ends: + + ; --- BEGIN INCLUDE: String.asm --- + ... library code ... + ; --- END INCLUDE: String.asm --- + + +INCLUDE SYNTAX +-------------- +To include a library, add the following line anywhere in your +file. It must be on its own line with a single space between +;#Include and the filename: + + ;#Include LibraryName.asm + +The filename is relative to the libs/ folder. Do not include +the path, only the filename: + + ;#Include Control.asm <- correct + ;#Include libs/Control.asm <- incorrect + + +KNOWN LIMITATIONS +----------------- + - The libs/ path is hardcoded. If you move the linker or + the libs folder, you will need to update the libsPath + variable at the top of linker.py. + - Circular includes (A includes B, B includes A) will be + caught by the duplicate detection and skipped, but the + order of resolution may be unexpected. + - Exactly two arguments are required. The linker will exit + with an error if given fewer or more. \ No newline at end of file diff --git a/Software_Assembly/ek-konis/linker.py b/Software_Assembly/ek-konis/linker.py new file mode 100644 index 0000000..4681c4d --- /dev/null +++ b/Software_Assembly/ek-konis/linker.py @@ -0,0 +1,106 @@ +import os +import sys + +""" +Scans a file recursivily for ;#Includes lib +and resolves depedencies +""" + +libsPath = "libs/" + +def CutUnmarkedRegions(filepath): + keywordLibStart = ";START OF LIB" + keywordLibEnd = ";END OF LIB" + keywordInclude = ";#Include" # We need to keep these! + + try: + with open(filepath, 'r', encoding='utf-8') as f: + linhas = f.readlines() + + output = [] + in_lib_zone = False + + for linha in linhas: + # Always keep include lines, no matter where they are + if keywordInclude in linha: + output.append(linha) + continue + + # Standard guard logic + if keywordLibStart in linha: + in_lib_zone = True + continue # Skip the actual ";START OF LIB" line + if keywordLibEnd in linha: + in_lib_zone = False + break # Stop processing once the lib ends + + if in_lib_zone: + output.append(linha) + + return output + except Exception as e: + return [f"; Erro ao processar {filepath}: {e}\n"] +def StitchFiles(main_file, output_path): + keyword = ";#Include" + included_files = set() + + included_files.add(os.path.basename(main_file)) + + with open(main_file, 'r', encoding='utf-8') as f: + file_buffer = f.readlines() + + finished = False + while not finished: + found_include = False + + for i, linha in enumerate(file_buffer): + if keyword in linha: + target = linha.split(' ')[1].strip() + + # Check if we have already included this specific library + if target in included_files: + # Replace the include line with a warning comment instead of the code + file_buffer[i] = f"; --- SKIP DUPLICATE INCLUDE: {target} ---\n" + found_include = True + break + + lib_path = os.path.join(libsPath, target) + + # Mark as seen + included_files.add(target) + + lib_code = CutUnmarkedRegions(lib_path) + + replacement = [ + f"; --- BEGIN INCLUDE: {target} ---\n", + *lib_code, + f"; --- END INCLUDE: {target} ---\n" + ] + + file_buffer[i:i+1] = replacement + found_include = True + break + + if not found_include: + finished = True + + with open(output_path, 'w', encoding='utf-8') as f: + f.writelines(file_buffer) + +def main(): + + "Check for arguments" + if len(sys.argv) <= 2: + sys.exit("Plese Specify an input and output file") + if len(sys.argv) > 3: + sys.exit("Too many args") + + file = sys.argv[1] + outputfile = sys.argv[2] + + StitchFiles(file, outputfile) + + return 0 + +main() +