Skip to main content

A CPU framework

Project description

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

pip install angel-cpu

Now, let's start with some simple stuff, give me a second i'm setting up

from angel_cpu import *
ARCH=cpu("ARCH")

Alright now i'm done setting up. As you can see we have a class here

from angel_cpu import *
ARCH=cpu("ARCH")#THIS RIGHT HERE

Let's first set up the basic CPU ARCH

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

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

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

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

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

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

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

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?

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

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

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

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

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

from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.set_comment_symbol("symbol")

get_register: returns the value of the selected register, that's it.

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

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.

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

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.

from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.wait(1000)

scroll: scrolls the screen by the set amount

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

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

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

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

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

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

from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.events()

clear: clears the screen buffer, the changes don't update until frame is called

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

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

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

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

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

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

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

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

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

from angel_cpu import *
ARCH=cpu("ARCH")
ARCH.assemble("code")

run: runs the code in the program, the program is set by using assemble

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

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

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

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

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

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

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,

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

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...

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...

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...

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:

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

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

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

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.

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

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...

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

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

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

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

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

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

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

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

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

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

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,

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,

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

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

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))

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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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:

mov rxa 10
add rxa rxa
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

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

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

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

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

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

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

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,

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

from angel_cpu import *
ARCH=cpu("ARCH")
x=ARCH.add_disk("sector_size","sectors")
ARCH.attach_device("disk",x)
div=ARCH.get_device("disk")

And with that we should be done here! That's all for this update!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

angel_cpu-1.3.tar.gz (31.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

angel_cpu-1.3-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file angel_cpu-1.3.tar.gz.

File metadata

  • Download URL: angel_cpu-1.3.tar.gz
  • Upload date:
  • Size: 31.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for angel_cpu-1.3.tar.gz
Algorithm Hash digest
SHA256 841660b6e717bd3a235718bdfa278bdcf02881f5ccb1a1e352612295b256c0f3
MD5 410e79d67a21ab17f583b9defa600188
BLAKE2b-256 0131b41ab0b2d90ead4370b0388621821d1cba699caa0b9cb9243e66cfd1f10c

See more details on using hashes here.

File details

Details for the file angel_cpu-1.3-py3-none-any.whl.

File metadata

  • Download URL: angel_cpu-1.3-py3-none-any.whl
  • Upload date:
  • Size: 16.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for angel_cpu-1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 81a76737212dd85d13d2615bfb6b335a0724e298b2800ded1e70e2a61cd1c791
MD5 244735f9fe04ad77a0f032683b4957d6
BLAKE2b-256 8699b23bc5e16ef9f6d4d062a203fe7f827b12c0a5a674addfb06c9d0cf6aadd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page