Hello!\
Have you ever tried to make a cpu simulation but don't want to write 300+ lines of code?\
Well then you don't have to anymore!\
AngelCPU does most of the heavy lifting for you, so let's get started before I fall asleep while typing this.
# Getting Started
**Note that this is reference for versions up to ver 1.1, if you have a version higher than 1.1 go to the "Major update 1.2 version"**
First lets install Angel
```bash
pip install angel-cpu
```
Now, let's start with some simple stuff,
*give me a second i'm setting up*
```python
from angel_cpu import *
ARCH=cpu("ARCH")
```
*Alright now i'm done setting up.*
As you can see we have a class here
```python
from angel_cpu import *
ARCH=cpu("ARCH")#THIS RIGHT HERE
```
Let's first set up the basic CPU ARCH
```python
from angel_cpu import *
ARCH=cpu("ARCH")
# no reset does NOT reset the ARCH's registers and ram it just resets the ARCH's PC and Program
ARCH.reset()
# The name of the Register doesn't matter go crazy if you want
# The amount of Registers also don't matter, you can add like 50 of them and it wouldn't care
ARCH.add_register("Rxa")
ARCH.add_register("Rxb")
ARCH.add_register("Rxc")
ARCH.add_register("Rxe")
# the size is calculated by the x (400) times the y (200)
ARCH.add_ram(400,200)
# 32 bit integer limit
ARCH.set_max(0xffffffff)
# we're setting the command symbol to be ; so it's like real ASM
ARCH.set_comment_symbol(";")
```
*huff* that took me 30 minutes to write... (all of the time was because of the comments XD)\
Anyway... let's go over what the eclipse is all of these before my head explodes

`ARCH.reset()`

Resets the ARCH's PC and Program

`ARCH.add_register("Register name")`

adds a register

`ARCH.add_ram(400,200)`

set's the ram size which with this one will be... *give me a sec i'm calculating...* 80000 cells... *WOW*

`ARCH.set_max(0xffffffff)`

sets the integer limit, I just put the 32 bit limit because it's the bare minimum *trust me with this, you are going to need **much** more than 4294967295 bits **trust me***

`ARCH.set_comment_symbol(";")`

This Sets the comment symbol, any line that starts with it will be ignored in the assembler.\

Now that we got that covered it's time to add some opcodes so we can actually do stuff
```python
from angel_cpu import *
ARCH=cpu("ARCH")
# no reset does NOT reset the ARCH's registers and ram it just resets the ARCH's PC and Program
ARCH.reset()
# The name of the Register doesn't matter go crazy if you want
# The amount of Registers also don't matter, you can add like 50 of them and it wouldn't care
ARCH.add_register("Rxa")
ARCH.add_register("Rxb")
ARCH.add_register("Rxc")
ARCH.add_register("Rxe")
# the size is calculated by the x (400) times the y (200)
ARCH.add_ram(400,200)
# 32 bit integer limit
ARCH.set_max(0xffffffff)
# we're setting the command symbol to be ; so it's like real ASM
ARCH.set_comment_symbol(";")
def MOV(reg,value):
    ARCH.set_register(reg,value)
def LOG(txt):
    if txt in ARCH.registers:
        print(ARCH.registers[txt])
    else:
        print(txt)
# It's important to note that when adding opcodes don't put parentheses on the function
# ARCH.add_opcode(0x0,MOV()) <-- incorrect, paratheses arnt opposed to be in the opcodes 
# ARCH.add_opcode(0x0,MOV) <-- correct
ARCH.add_opcode(0x0,MOV)
ARCH.add_opcode(0x1,LOG)
c="""
MOV Rxa 10
LOG Rxa
"""
ARCH.assemble(c)
ARCH.run()
```
Okay there's a lot of stuff here so let's talk about the things that changed

`ARCH.add_opcode(hex_ID function)`

This adds an opcode, and when adding the function, please, **do not add parentheses**

`ARCH.assemble(code)`

This assembles the code from ASM format into something that the CPU can actually understand, then it loads it into the CPU's program ready to run.

`ARCH.run()`

This takes the code from the Program and runs it, simple and very useful.

Now let's talk about the ASM format
```AngelASM
MOV Rxa 10
LOG Rxa
```
The command comes first, each argument comes after the command separated by spaces, but then you might be wondering, "what about strings with spaces in them?" well, strings are strings so there has to be these (") surrounding them, without that it wouldn't be a string!

Alright this is taking a long time so i'll just boot something give me a sec...

**\[BIOS\]: BOOTING CMD DUMP...**

**\[BIOS\]: INIT REG**

**\[REG\]: \[OK\]**

**\[BIOS\]: INIT SD**

**\[SD\]: \[OK\]**

**\[BOOTING ASM_HELPER\]: \[OK\]**

**\[ASM_HELPER\]: \[OK\]**

Alright, I booted ASM_helper to speed things along, here's the rest of the commands along with some stuff I already covered

`add_register`:
adds a register, there's not much to say about this, but registers can be named anything, however their *value* cannot go over the integer limit, we'll go over how to set that in a bit
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.add_register("register")
```
`add_ram`:
sets the ram, the size is calulated by the X times the Y however the RAM is a 1D plane, so you can just use `get_ram(addr)` with the addr being an int
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.add_ram("size_x","size_y")
```
`add_opcode`:
adds an opcode for a function, please note that commands must be inputted without parentheses or you RISC *(haha get it?)* making the commands have set arguments
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.add_opcode("opcode","commmand")
```
`add_instruction`:
This is for those of you who want to make your own assembler, or those of you who just want to add instructions without the assembler
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.add_instruction("opcode")
```
`set_window_name`:
This sets the Window's name, and as a side note the window's name starts as the arch's name, but you can set this to what ever you want
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.set_window_name("name")
```
`set_max`:
sets the integer limit, it can be any type of int, like hex, and i recomend using hex because it's much easier to write large numbers in hex, for example 0xffffffff in decimal is 4294967295, now you see what i mean?
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.set_max("number")
```
`set_pc`:
sets the PC, for those of you who aren't fluent in tech, PC means program counter, it tells the CPU what instruction it's meant to be looking at, and it's very useful with making commands like JMP
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.set_pc("number")
```
`set_register`: sets a register's value, however if the register's value overflows to more than the integer limit the register's value resets back to it's base, depending on the type of data the register was holding the fallback will be different, for example
if it was holding a string, and the string's length was longer than the max interger limit it falls back to an empty string ("") the same is for lists, dicts, tuples, and ints, with ints having a fallback of 0x0
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.set_register("register","value")
```
`set_ram`:
sets a ram cell's value, if the value is larger than the integer limit it will fall back to empty, with lists,dicts, and tuples they will fallback to and empty version of themselves with ints falling back to 0x0
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.set_ram("address","value")
```
`reset`:
resets the PC and the program of the CPU, with the PC resetting to 0x0 and the program resetting back to none
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.reset()
```
`set_comment_symbol`:
sets the command symbol, if you where to set it to ; any line starting with ; would be commented out and ignored by the assembler
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.set_comment_symbol("symbol")
```
`get_register`:
returns the value of the selected register, that's it.
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.get_register("register")
```
`get_ram`:
gets the value of the selected ram cell, that's it
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.get_ram("address")
```
`get_pc`:
returns the value of the PC of the CPU, that's it.
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.get_pc()
```
`text`:
prints text onto the screen window, the `text` will not render until `frame` is called, the AddToTxtLog input must either be `True` or `False`, `False` means when you write `text_input` the text will not show up on the screen, while `True` means it will, the `end` input is what will be put on the end of the text, if end is not `"\n"` then it will not create a new line when drawing, the first input in the `customy` input must either be `True` or `False`, `True` means `customy` is enabled, if it is False then `customy` will not be enabled, the second input for `customy` is the y value, note that the y value will only be that if `customy` is enabled
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.text("text","x_pos",("color_R","color_G","color_B"),"Add_to_text_log","end",("Is customy enabled?","y_pos"))
```
`wait`:
waits for the set amount of time, note that the time is in milliseconds.
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.wait(1000)
```
`scroll`:
scrolls the screen by the set amount
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.scroll("X","Y")
```
`is_Mouse_Touching_Surface`:
it returns True is the mouse if touching a surface, else, it returns False
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.is_Mouse_Touching_Surface("name")
```
`show_mouse`:
it sets the mouse's visiblity, if true it shows the mouse, if false it doesn't else? it breaks, it must either be true or false
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.show_mouse("True, or False")
```
`quit`:
when this command is called it quits the screen and the app, i recommend only using it when you need to close EVERYTHING
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.quit()
```
`frame`:
displays the next frame on the screen window, that's it, it just displays the next frame
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.frame()
```
`load_pixels`:
loads the pixels from the pixel buffer to the screen, it needs frame to display the changes to the screen
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.load_pixels()
```
`events`:
events returns the events that happened with key presses it returns kDWN and then the key that's pressed so for e it would return kDWNe, for key release it will return kUP so releasing the key e would make it return kUPe, for mouse down it would return a tuple, with the first being mDWN and the second being another tuple containing the mouse X and Y, when the mouse is UP it returns mUP with the pos following
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.events()
```
`clear`:
clears the screen buffer, the changes don't update until frame is called
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.clear()
```
`clear_logs`:
clears the screen logs, this makes it useful with `text_input` because it stops trailing text
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.clear_logs()
```
`is_held`:
it returns true if the inputted key is held down, else it returns false
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.is_held("key")
```
`render_surfaces`:
This renders the surfaces, you have to call frame to see the effects though
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.render_surfaces()
```
`add_surface`:
This adds a surface, you have to call render_furfaces and frame to see them
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.add_surface("name","width","height",("color_R","color_G","colo_rB"),"pos_X","pos_Y")
```
`add_pixel`:
adds a pixel, you have to call load_pixels and frame to see the effects
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.add_pixel("pos_x","pos_y",("color_R","color_G","colo_rB"),"size","group","addtopixellog")
```
`text_input`:
Text_input displays text on the screen as a prompt, then after the key enter is pressed, it returns what the user typed
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.text_input("prompt",("color_R","color_G","colo_rB"),"prompt_end","prompt_x")
```
`mouse_down_on_group`:
This returns True if the mouse has clicked a pixel group
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.mouse_down_on_group("event output","group")
```
`mouse_pos`:
returns the mouse pos in a tuple
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.mouse_pos()
```
`assemble`:
one of the most useful commands in angel, it compiles your ASM code into opcodes so your CPU can run it, now it compiles ASM not the opcodes themselves so don't go inputting 0x0 for MOV or 0x1 for JMP that just won't work
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.assemble("code")
```
`run`:
runs the code in the program, the program is set by using assemble
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.run()
```
`0.7 commands`:
the following commands are the new commands added in angel-cpu 0.7

**\[SD\]: LOADING 0.7 COMMANDS**

**\[SD\]: \[OK\]**

`add_disk`:
Adds a disk, add_disk returns a disk class, using this you can create disks and persisting storage, if you are trying to use the ARCH object with the disk use `attach_device`
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
```
**\[SD\]: found multiple entries in the `Disk` class, go over them as well?**

uh... sure...

**\[SD\]: Loading `Disk` sub commands...**

`write`:
writes to the inputted address, note that the address is a tuple, with the first input being the sector and the second being the cell
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
x.write(("addr-sector","addr_cell"),"data")
```
`read`:
reads from the inputted address, note that the address is a tuple, with the first input being the sector and the second being the cell
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
x.read(("addr_sector","addr_cell"))
```
`format`:
formats the Disk, returns the Disk back to when it was first defined
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
x.format()
```
`dump`:
dumps the disk, this dumps the disk to a file, the `name` input is the name of the file, so i recomend you select one name and stick with it
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
x.dump("name")
```
`load`:
loads the disk from the inputted file, note that the name must be exactly what the Disk's file name is
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
x.load("name")
```

**\[SD\]: `Disk` sub commands completed, returning back to CPU commands...**

`attach_device`:
attaches a device to the CPU, the devices can be anything, extra storage, disks, and if you wanted to, you could add an entire second CPU as an device, but i recommend you don't do that, it's hard to manage two CPUs a once,
```python
from angel_cpu import *
ARCH=cpu("ARCH")
device=ARCH.add_disk("sector_size","sectors")
ARCH.attach_device("name",device)
```
`get_device`:
returns the inputted device, this one returns the inputted device's class object, but like how not everything is a potato, not all devices are class based, so it returns whatever the device is
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
ARCH.attach_device("disk",x)
div=ARCH.get_device("disk")
```
`0.9 commands`:
the following commands are the new commands added in angel-cpu 0.9

**\[SD\]: LOADING 0.9 COMMANDS**

**\[SD\]: \[OK\]**

`set_multiline_comment_symbol_start`:
sets the multiline comment symbol start, not much to say here just don't try to put comments in comments it doesn't work...
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.set_multiline_comment_symbol_start("<:")
```

`set_multiline_comment_symbol_end`:
sets the multiline comment symbol end, not much to say here, just don't try to put comments in comments it doesn't work...
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.set_multiline_comment_symbol_end(":>")
```
# Major update 1.2
Alright in 1.2 a LOT changed, so much changed to the point that i had to rewrite half of Angel's code to fit in the upgrades, and since so much changed, we **have** to go over everything again, so let's jump right in!

*One second, setting up a base ARCH...*
```python
from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.set_max(0xffff) #16 bit int limit
ARCH.set_comment_symbol("//")
ARCH.set_multiline_comment_symbol_start("/*")
ARCH.set_multiline_comment_symbol_end("*/")
ARCH.add_ram(1240) #Around a KB of ram

ARCH.add_register("ixa",int) #int-only register
ARCH.add_register("ixb",int) #int-only register
ARCH.add_register("ixc",int) #int-only register
ARCH.add_register("ixd",int) #int-only register

def mov(reg,val):
    ARCH.set_register(reg,val)
def log(txt,val):
    if txt in ARCH.registers:
        print(ARCH.get_register(txt))
    elif txt.startswith("#RAM#"):
        print(ARCH.get_ram(txt[5:]))
    else:
        print(txt)
def stor(addr,val):
    ARCH.set_ram(addr,val)
ARCH.add_opcode(0x0,mov)
ARCH.add_opcode(0x1,log)
ARCH.add_opcode(0x2,stor)
c="""
mov ixa 0
mov ixb 0
mov ixc 0
mov ixd 0

mov ixa 1
stor 0 "hello!"

log ixa
log #RAM#0
"""
ARCH.assemble(c)
ARCH.run()
```
Alright, now you might have noticed that when i was setting the registers there was an extra input, and you would be right to notice that because now registers have set types, where the type of data the register holds is set and can't be changed, if you try to put in a different type than what the register's type was you will get an error, however i'm not that strict if you put the type to be `None` then it will be a "general purpose" register, that can hold any of the supported types of data, now lets actually get into the commands.

`add_register`:
adds a register, the first input is the name of the register with the second being the type, the available types in 1.2 are: `int`, `str`, `bool`, `float`, `bytes`, `list`, `dict`, `set`, `tuple`, and `None`; as i explained above, register values are now *set* meaning at runtime they can't change their value type at runtime unless their type was set to `None`, wich then you can give them any type of value, as an example:
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xffff)
ARCH.add_register("nxa")
ARCH.add_register("ixa")
ARCH.set_register("nxa",10) #Okay, general purpose registers can have any value
ARCH.set_register("ixa", "hello") #incorrect will raise a TypeError
```
As you can see register `nxa` has value of 10 and since it's type is `None` it doesn't break, however register `ixa` has a `int` typing, because of this setting its value to `"hello"` will raise a `TypeError`, now let's get to the next one, we don't have all day!

`add_ram`:
Adds ram: add_ram sets the ram's size to the input, in previous versions, add_ram needed two inputs, but now add_ram only needs one, unlike registers which have **set** typing RAM has **dynamic** typing where you can set the ram's type to anything and it changes at runtime, so you could give it an int to hold and then give it an string and it wouldn't care less
```python
from angel_cpu import *
ARCH=cpu()
ARCH.add_ram(1240)
```

`add_opcode`: adds an opcode, this hasn't changed much since the last version, but just to remind you, the first input is the opcode's ID, and the second is the command to run, **please do not add  paratheses when inputing the command it will break it**
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
def mov(t,val):
    ARCH.set_register(t,val)
ARCH.add_opcode(0x0,mov)
```

`add_instruction`:
adds an instruction, this is usefull if you want to make your own assembler, however if you're just using it raw i'm going to warn you, **you have to put the opcode IDs directly**
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
def mov(t,val):
    ARCH.set_register(t,val)
ARCH.add_opcode(0x0,mov)
ARCH.add_instruction((0x0,("ixa",10)))
```

`set_max`:
sets the integer limit, but just because the value that you're trying is a str doesn't mean it's not going to be blocked, yeah, the int limit also plies to the rest of the group with length if something has a length value larger than the max interger limit, it's going to be reset back to it's type, and yes, this applies to RAM as well.
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(10)
```

`set_pc`:
sets the pc (program counter) to the inputted value, this didn't change but it's still important and useful so it's here
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.set_pc(0)
```

`set_register`: this sets the register to the inputted value, this didn't change much...
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)

ARCH.set_register("sxa", "hello!")
```

`set_ram`:
not to be confused with add_ram, set_ram sets the inputted address' value, the first input is the address and the second is the value
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.set_ram(0,10)
```

`reset`:
resets the pc and program, that's literally it, and it didn't change since the last update
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.reset()
```

`set_comment_symbol`:
sets the comment symbol to the inputted value, didn't change since last update, Note that this comment only works at the start of the line, **This was changed in version 1.5**
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.set_comment_symbol("//")
c="""
//this is a comment
// this too!
"""
```

`set_multiline_comment_symbol_start`:
sets the symbol for the multiline comment start, didn't change at all
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.set_multiline_comment_symbol_start("/*")
ARCH.set_multiline_comment_symbol_end("*/")
c="""
/*
this is multiline!
*/
"""
```

`set_multiline_comment_symbol_end`:
sets the symbol for the end of the multiline comment, literally didn't change at all

```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.set_multiline_comment_symbol_start("/*")
ARCH.set_multiline_comment_symbol_end("*/")

c="""
/*
this is a comment!!!
*/
"""
```

`register_exists`:
returns True if the inputted register exists, that's literally it
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
if ARCH.register_exists("ixa"):
    print("hello!")
```

`register_type`:
returns the type of the inputted register, that's it
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
print(ARCH.register_type("ixa"))
```

`ram_size`:
returns the size of the RAM, that's it nothing else
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
print(ARCH.ram_sieze)
```

`get_register`:
returns the value of the inputted register
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
print(ARCH.get_register("ixa"))
```

`get_ram`:
returns the value of the address in ram, ... too confusing? it just returns the value in the address inside of the ram, if address 0 was 10 and we used this; `get_ram(0)`  it would return 10
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_ram(1240)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.set_ram(0,10)
print(ARCH.get_ram(0))
```

`get_pc`:
returns the current PC value, didn't change much since the last update,
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
print(ARCH.get_pc())
```

`clear`:
clears the screen window, that's it,
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.clear()
```

`clear_logs`:
clears the screen logs, it just clears the screen logs, so when `input` is called it previous text from print is not seen, **it does not clear the screen it only clears the logs**
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.clear()
ARCH.print("hello!",0,0)
ARCH.clear_logs()
```

`frame`:
needed for any changes to the screen to actually be displayed
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.print("hello!",0,0)
ARCH.frame()
```

`events`:
this command changed a lot since the last update, but the core principle staying the same, this time it returns `("keydown",keyName)` on a key press with the keyName being the name of the key, the same is true for key release `("keyup",keyName)`, with mouse clicks it returns `("mousedown",(mouse_x_pos,mouse_y_pos))` with mouse_x_pos being the x pos of the mouse and mouse_y_pos being the y pos of the mouse, and yes, it's in a tuple, with mouse release mirroring this, with it instead returning `("mouseup",(mouse_x_pos,mouse_y_pos))`
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
e=ARCH.events()
print(e)
```

`shift_key`:
this command returns the shifted key with the input, for clearer terms, if you input "a" it would return "A" and if you inputted "[" it would return "{" note that it will raise an Error if the input is not a single character.
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.shift_key("a")
```

`set_font`:
this one is a BIG new feature in this one, it sets the `print` and `input`'s font to the input, and that's not all it can switch between installed fonts and external fonts with it's `mode` input, if the mode is 1 then it will read from a font file, if the mode is 2 then it will use an installed font, the second input is the name of the font or the name of the file, and the last is the size input which controls the size of the font
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)

ARCH.set_font(2,"consolas",10)
```

`print`:
prints text onto the screen, that's it
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.print("text","x","y",("color_R","color_G","color_B"),"add_to_text_log","blit_surface")
```

`input`:
displays a text input, this got a huge upgrade from before, but the inputs are mainly the same
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
print(ARCH.input("prompt","base prompt","x","y",("color_R","color_G","color_B"),"blit_surface"))
```

`add_sprite`:
adds a sprite, the only input is the name
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_sprite("name")
```

`render_sprites`:
renders the sprites that's it
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_sprite("name")
ARCH.render_sprites()
```

`move_sprite`:
moves the inputted sprite
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_sprite("name")
ARCH.move_sprite("name")
```

`set_sprite_pos`:
sets the inputted sprite's pos
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_sprite("name")
ARCH.set_sprite_pos("name")
```

`set_sprite_image`:
sets the inputted sprite's image, note that the image has to be in the same folder as the script
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_sprite("name")
ARCH.set_sprite_image("name","image")
```

`kill_sprite`:
"kills" the inputted sprite, don't worry it just removes it
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_sprite("name")
ARCH.kill_sprite("name")
```

`sprite_touching_sprite`:
returns true if sprite_a is touching sprite_b
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_sprite("name")
print(ARCH.sprite_touching_sprite("sprite_a","sprite_b"))
```

`sprite_touching_mouse`:
returns if the inputted sprite is touching the mouse
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_sprite("name")
print(ARCH.sprite_touchng_mouse("name"))
```

`sprite_click`:
returns true if the sprite was clicked, needs the event output from `events` to work
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
e=ARCH.events()
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_sprite("name")
print(ARCH.sprite_click("name",e))
```

`scale_sprite`:
scales the inputted sprite by the width and height
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_sprite("name")
ARCH.scale_sprite("name","width","height")
```

`surface`:
creates a new surface, that's it
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_surface("name","x","y","width","height",("color_R","color_G","colorB"))
```

`render_surfaces`:
renders the surfaces made
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_surface("name","x","y","width","height",("color_R","color_G","colorB"))
ARCH.render_surfaces()
```

`surface_click`:
returns true if the inputted surface was clicked, needs `events`' output to work
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_surface("name","x","y","width","height",("color_R","color_G","colorB"))
print(ARCH.surface_click("name"))
```
`surface_touching_mouse`:
returns true if the inputted surface is touching the mouse
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_surface("name","x","y","width","height",("color_R","color_G","colorB"))
print(ARCH.surface_touching_mouse("name"))
```

`kill_surface`:
"kills" the inputted surface, don't worry, it just removes the surface
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_surface("name","x","y","width","height",("color_R","color_G","colorB"))
ARCH.kill_surface("name")
```

`quit`:
closes the program
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.quit()
```
`wait`:
waits for the inputted time in milliseconds
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.wait("time")
```

`set_window_name`:
sets the windows name
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.set_window_name("name")
```

`set_window_icon`:
sets the window icon, the icon has to be in the same folder as the script
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.set_window_icon("file name")
```

`mouse_down`:
returns true if the mouse is held
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
print(ARCH.mouse_down())
```

`is_held`:
returns true if the inputted key is held
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
print(ARCH.is_held("k"))
```

`mouse_pos`:
returns the mouse's pos in a tuple
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
print(ARCH.mouse_pos())
```

`add_sound`:
adds a sound to the buffer `play_sound` must be called for the sound to be played
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.add_sound("file name")
```

`play_sound`:
plays the inputted sound if it's in the sound buffer
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.play_sound("name")
```

`stop_sound`:
stops the inputted sound
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
ARCH.stop_sound("name")
```

`assemble`:
assembles the inputted code remember the asm format is this no commas: **this was changed in ver 1.5 goto "Format update 1.5" to see the changes**
```asm
mov rxa 10
add rxa rxa
```
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
def mov(reg,val):
    ARCH.set_register(reg,val)
ARCH.add_opcode(0x0,mov)
c="""
mov ixa 0
mov sxa ""
mov nxa ""
"""
ARCH.assemble(c)
```

`run`:
runs the assembled code, you need to call assemble for it to work
```python
from angel_cpu import *
ARCH=cpu()
ARCH.set_max(0xff)
ARCH.add_register("ixa",int)
ARCH.add_register("sxa",str)
ARCH.add_register("nxa",None)
def mov(reg,val):
    ARCH.set_register(reg,val)
ARCH.add_opcode(0x0,mov)
c="""
mov ixa 0
mov sxa ""
mov nxa ""
"""
ARCH.assemble(c)
ARCH.run()
```

`add_disk`:
Adds a disk, add_disk returns a disk class, using this you can create disks and persisting storage, if you are trying to use the ARCH object with the disk use `attach_device`
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
```
**\[SD\]: found multiple entries in the `Disk` class, go over them as well?**

uh... sure...

**\[SD\]: Loading `Disk` sub commands...**

`write`:
writes to the inputted address, note that the address is a tuple, with the first input being the sector and the second being the cell
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
x.write(("addr-sector","addr_cell"),"data")
```
`read`:
reads from the inputted address, note that the address is a tuple, with the first input being the sector and the second being the cell
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
x.read(("addr_sector","addr_cell"))
```
`format`:
formats the Disk, returns the Disk back to when it was first defined
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
x.format()
```
`dump`:
dumps the disk, this dumps the disk to a file, the `name` input is the name of the file, so i recomend you select one name and stick with it
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
x.dump("name")
```
`load`:
loads the disk from the inputted file, note that the name must be exactly what the Disk's file name is
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
x.load("name")
```

**\[SD\]: `Disk` sub commands completed, returning back to CPU commands...**

`attach_device`:
attaches a device to the CPU, the devices can be anything, extra storage, disks, and if you wanted to, you could add an entire second CPU as an device, but i recommend you don't do that, it's hard to manage two CPUs a once,
```python
from angel_cpu import *
ARCH=cpu("ARCH")
device=ARCH.add_disk("sector_size","sectors")
ARCH.attach_device("name",device)
```
`get_device`:
returns the inputted device, this one returns the inputted device's class object, but like how not everything is a potato, not all devices are class based, so it returns whatever the device is
```python
from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
ARCH.attach_device("disk",x)
div=ARCH.get_device("disk")
```
# Format update 1.5
Okay in 1.5 the original format has been changed as the assembler has been changed to be less buggy

The new format is this:
```ASM
mov sxa, "string"
mov ixa, 0x0
mov nxa, "anything"
```
You see, i spent a good three hours changing the assembler so the syntax is cleaner, and comments, lists, dicts, tuples finally work, now, some syntax rules

```ASM
mov sxa, "string" ; comments (finally) fully work!
mov ixa, 0x0 <: mutliline as well! :>
mov nxa, "anything"
```
each input HAS to have commas separating them, the last input shouldn't have a comma:
```ASM
cjmp 10, ==, 10, 0
```
Anyway, that's all for this update!