├── .gitignore ├── BUBBLE2.dripc ├── echo.dripc ├── print.bean ├── echo.drip ├── ram.py ├── input.bean ├── Grind.py ├── BUBBLE2.drip ├── README.md ├── docs ├── preproc.md ├── runtime.md └── isa.md ├── registers.py ├── QuadShot.py └── Drip.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .idea 3 | .DS_Store -------------------------------------------------------------------------------- /BUBBLE2.dripc: -------------------------------------------------------------------------------- 1 | { 2 | "subs": [ 3 | "print" 4 | ], 5 | "switches": { 6 | "mode": "save" 7 | } 8 | } -------------------------------------------------------------------------------- /echo.dripc: -------------------------------------------------------------------------------- 1 | { 2 | "subs": [ 3 | "print", 4 | "input" 5 | ], 6 | "switches": { 7 | "mode": "save" 8 | } 9 | } -------------------------------------------------------------------------------- /print.bean: -------------------------------------------------------------------------------- 1 | .sub(PRINT): ; 2 | POPG CL ; 3 | CONT: ; 4 | CMP [CL],000A ; Is CL at EOL (end of string delimiter)? 5 | JZ RETURN ; If yes, exit subprogram 6 | OUT [CL] ; If no, output character at [CL] to stdout 7 | INC CL ; 8 | JMP CONT ; 9 | RETURN: ; 10 | RET 0001 ; 11 | .endsub: ; -------------------------------------------------------------------------------- /echo.drip: -------------------------------------------------------------------------------- 1 | .data_ ; 2 | string>text="this will be overwritten"; 3 | literal>len=0020 4 | .exec_ ; 5 | %input% 6 | %print% 7 | MOV BL,0000 ; 8 | MOV CH,len ; 9 | MOV DL,0000 ; 10 | MAIN: ; 11 | MOV CL,text ; 12 | PUSHG CL ; 13 | CALL INPUT ; 14 | POPG BH ; 15 | POPG AL ; remove newline from stack 16 | MOV CL,text ; 17 | ADD CL,BH ; 18 | MOV [CL],AL ; store newline at end 19 | DEC CL ; point to last char index 20 | STORE: ; 21 | CMP BH,0000 ; 22 | JZ PRNT ; 23 | POPG AL ; 24 | MOV [CL],AL ; 25 | DEC CL ; 26 | DEC BH ; 27 | JMP STORE ; 28 | PRNT: ; 29 | MOV CL,text ; 30 | PUSHG CL ; 31 | CALL PRINT ; 32 | END ; -------------------------------------------------------------------------------- /ram.py: -------------------------------------------------------------------------------- 1 | import Drip 2 | 3 | 4 | class RAM: 5 | """Simple RAM interface and storage object""" 6 | def __init__(self): 7 | self.image = [['0000' for _ in range(256)] for _ in range(256)] 8 | 9 | def get(self, location: str) -> str: 10 | x, y = divmod(int(location, 16), 256) 11 | return self.image[x][y] 12 | 13 | def put(self, location: str, data: str): 14 | if data == 'C1': 15 | print(location, data) 16 | x, y = divmod(int(location, 16), 256) 17 | self.image[x][y] = data.zfill(4).upper() 18 | 19 | def show(self): 20 | print(' '+' '.join([Drip.tohex(x).zfill(4).upper() for x in range(256)])) 21 | for y, row in enumerate(self.image): 22 | print(Drip.tohex(y*256), end=' ') 23 | for item in row: 24 | print(item, end=' ') 25 | print('') 26 | 27 | 28 | if __name__ == "__main__": 29 | _a = RAM() 30 | _a.put('010E', '12') 31 | _a.show() 32 | print(_a.get('11')) 33 | -------------------------------------------------------------------------------- /input.bean: -------------------------------------------------------------------------------- 1 | .sub(INPUT): ; 2 | PUSH AL ; store contents of AL in private stack 3 | PUSH CL ; store contents of CL in private stack 4 | PUSH CH ; store contents of CH in private stack 5 | POPG CL ; get max length of input to be read 6 | MOV CH,0000 ; set CH to 0 7 | CONT: ; 8 | CMP CL,0000 ; if CL is 0, then we are done 9 | JZ RETURN ; if so, return 10 | IN AL ; Read segment from stdin and store in AL 11 | PUSHG AL ; store contents of AL in global stack 12 | CMP AL,000A ; If end of line, 13 | JZ RETURN ; then return, otherwise 14 | DEC CL ; decrement CL 15 | INC CH ; increment CH 16 | JMP CONT ; and go back to the top of the loop 17 | RETURN: ; 18 | PUSHG CH ; store contents of CH in global stack 19 | POP CH ; restore contents of CH from private stack 20 | POP CL ; restore contents of CL from private stack 21 | POP AL ; restore contents of AL from private stack 22 | RET 0001 23 | .endsub: ; -------------------------------------------------------------------------------- /Grind.py: -------------------------------------------------------------------------------- 1 | # Grind is the assembly pre-processor for Drip, featuring an interactive mode for command line setup of the Drip system 2 | import json 3 | 4 | 5 | def verify_dict(check, args, default=None): 6 | for arg in args: 7 | if arg not in check: 8 | check[arg] = default 9 | return check 10 | 11 | 12 | def parse_dict(kwargs, arg_list, default=None): 13 | args = verify_dict(kwargs, arg_list, default) 14 | return (args[x] for x in arg_list) 15 | 16 | 17 | class Grind: 18 | def __init__(self, **args): 19 | self.file, self.config = parse_dict(args, ['file', 'config'], '') 20 | self.file = open(self.file) 21 | self.file = [line for line in self.file] 22 | config = open(self.config) 23 | self.switches, self.subs = parse_dict(json.load(config), ['switches', 'subs'], dict()) 24 | 25 | def sub(self): 26 | if self.subs: 27 | for s in self.subs: 28 | index = 0 29 | max = len(self.file) 30 | result = open(s + '.bean') 31 | result = [line for line in result] 32 | while index < max - 1: 33 | if '%' + s + '%' in self.file[index]: 34 | self.file = self.file[:index + 1] + result + self.file[index + 1:] 35 | max += len(result) 36 | index += 1 37 | 38 | def out(self): 39 | return self.file 40 | -------------------------------------------------------------------------------- /BUBBLE2.drip: -------------------------------------------------------------------------------- 1 | .data_ ; 2 | string>sort="zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFBECDA9876543210" 3 | literal>len=003E 4 | .exec_ ; 5 | %print% 6 | MOV BL,0000 ; 7 | MOV CL,sort ; 8 | MOV DL,0000 ; 9 | MAIN: ; 10 | MOV AL,len ; 11 | MOV BL,[AL] ; 12 | CMP DL,BL ; 13 | JZ BYE ; Exit if list is sorted 14 | CMP [CL],000A ; Is the display pointer at the end of the list? 15 | JZ RESET ; If yes, jump to reset label 16 | MOV AH,[CL] ; Move value at display pointer to AL 17 | INC CL ; Increment display pointer to get next value 18 | MOV AL,[CL] ; Move value at display pointer to BL 19 | CMP AL,000A ; 20 | JZ RESET ; 21 | CMP AH,AL ; Is AL greater than BL? 22 | JNS SWAP ; If yes, jump to swap label 23 | CHECK: ; 24 | INC DL ; Increment swap counter 25 | JMP MAIN ; Jump back to main loop 26 | SWAP: ; 27 | DEC DL ; Decrement swap counter 28 | SWP AX ; Swap values in AH and AL 29 | DEC CL ; Decrement display pointer 30 | MOV [CL],AH ; Move AL to [CL] 31 | INC CL ; Increment display pointer 32 | MOV [CL],AL ; Move BL to [CL] 33 | JMP MAIN ; Jump back to main loop 34 | RESET: ; 35 | MOV CL,sort ; Reset display pointer to C0 36 | JMP MAIN ; Jump back to main loop 37 | BYE: ; 38 | MOV CL,sort ; Re-initialize display pointer 39 | PUSHG CL ; 40 | CALL PRINT ; 41 | END ; End program -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A CPU and Assembly compiler written in Python 2 | 3 | ## Overview 4 | 5 | This project consists of a custom CPU, assembler, and runtime environment implemented in Python. The components include: 6 | 7 | 1. **Instruction Set Architecture (ISA)**: 8 | - Defined in `Drip.py` and `QuadShot.py`. 9 | - Instructions for arithmetic, jumps, data movement, comparison, stack operations, input/output, and subroutine calls. 10 | 11 | 2. **Assembler**: 12 | - Implemented in `Drip.py`. 13 | - Functions for tokenizing, parsing, and converting assembly code into machine code. 14 | 15 | 3. **CPU Runtime**: 16 | - Defined in `QuadShot.py`. 17 | - Includes the `CPU` class with methods for executing instructions, managing registers, and handling input/output. 18 | 19 | 4. **RAM**: 20 | - Defined in `ram.py`. 21 | - Provides a simple interface for memory storage and retrieval. 22 | 23 | 5. **Registers**: 24 | - Defined in `registers.py`. 25 | - Manages the CPU registers, supporting single, double, and adjacent register operations. 26 | 27 | ## Files 28 | 29 | - `Drip.py`: Contains the ISA definition and assembler implementation. 30 | - `QuadShot.py`: Contains the CPU runtime implementation. 31 | - `ram.py`: Contains the RAM class for memory storage and retrieval. 32 | - `registers.py`: Contains the Registers class for managing CPU registers. 33 | - `Grind.py`: Contains the assembly pre-processor for Drip. 34 | - `BUBBLE2.drip`: Example assembly code for a bubble sort algorithm. 35 | - `echo.drip`: Example assembly code for an echo program. 36 | - `print.bean`: Subroutine for printing strings. 37 | - `input.bean`: Subroutine for reading input. 38 | 39 | ## Usage 40 | 41 | To run the CPU with an assembly program, use the following command: 42 | 43 | ```sh 44 | python QuadShot.py 45 | ``` 46 | 47 | - ``: The name of the assembly program file (without the `.drip` extension). 48 | - ``: (Optional) Input string to be provided to the program. 49 | 50 | Example: 51 | 52 | ```sh 53 | python QuadShot.py BUBBLE2 "example input" 54 | ``` 55 | 56 | This command will run the `BUBBLE2.drip` program with the input "example input". 57 | -------------------------------------------------------------------------------- /docs/preproc.md: -------------------------------------------------------------------------------- 1 | # Preprocessor Documentation 2 | 3 | ## Overview 4 | 5 | The preprocessor is responsible for processing assembly code before it is passed to the assembler. It handles tasks such as macro substitution, conditional assembly, and file inclusion. The preprocessor is implemented in the `Grind.py` file. 6 | 7 | ## Grind Class 8 | 9 | The `Grind` class is the main class for the preprocessor. It provides methods for loading and processing assembly code. 10 | 11 | ### Methods 12 | 13 | - `__init__(self, **args)`: Initializes the `Grind` object with the specified arguments. 14 | - `file`: The assembly file to be processed. 15 | - `config`: The configuration file for the preprocessor. 16 | 17 | - `sub(self)`: Processes the assembly code for macro substitution. 18 | - If the `subs` attribute is not empty, it iterates through the list of macros and substitutes them in the assembly code. 19 | 20 | - `out(self)`: Returns the processed assembly code. 21 | 22 | ## Examples 23 | 24 | ### Example 1: Basic Usage 25 | 26 | ```python 27 | from Grind import Grind 28 | 29 | # Initialize the preprocessor with the assembly file and configuration file 30 | preprocessor = Grind(file='example.drip', config='example.dripc') 31 | 32 | # Perform macro substitution 33 | preprocessor.sub() 34 | 35 | # Get the processed assembly code 36 | processed_code = preprocessor.out() 37 | ``` 38 | 39 | ### Example 2: Configuration File 40 | 41 | The configuration file is a JSON file that specifies the macros and switches for the preprocessor. Here is an example configuration file: 42 | 43 | ```json 44 | { 45 | "subs": [ 46 | "print", 47 | "input" 48 | ], 49 | "switches": { 50 | "mode": "save" 51 | } 52 | } 53 | ``` 54 | 55 | ### Example 3: Assembly File 56 | 57 | The assembly file contains the assembly code to be processed. Here is an example assembly file: 58 | 59 | ```assembly 60 | .data_ 61 | string>text="Hello, World!" 62 | literal>len=0010 63 | .exec_ 64 | %print% 65 | MOV AX, text 66 | CALL PRINT 67 | END 68 | ``` 69 | 70 | ## Purpose and Benefits 71 | 72 | The preprocessor simplifies the assembly code by handling repetitive tasks and providing a way to include external files and macros. This makes the assembly code more readable and maintainable. The preprocessor also allows for conditional assembly, which enables the inclusion or exclusion of code based on certain conditions. 73 | -------------------------------------------------------------------------------- /docs/runtime.md: -------------------------------------------------------------------------------- 1 | # Runtime Environment Documentation 2 | 3 | ## Overview 4 | 5 | The runtime environment is responsible for executing the machine code generated by the assembler. It manages the CPU, memory, and input/output operations. The runtime environment is implemented in the `QuadShot.py` file. 6 | 7 | ## CPU Class 8 | 9 | The `CPU` class is the main class for the runtime environment. It provides methods for loading and executing machine code. 10 | 11 | ### Methods 12 | 13 | - `__init__(self)`: Initializes the `CPU` object and sets up the registers, memory, and input/output buffers. 14 | 15 | - `math(self, func, args)`: Performs arithmetic operations on the given arguments using the specified function. 16 | 17 | - `jmp(self, arg)`: Jumps to the specified address. 18 | 19 | - `jz(self, arg)`: Jumps to the specified address if the zero flag is set. 20 | 21 | - `jnz(self, arg)`: Jumps to the specified address if the zero flag is not set. 22 | 23 | - `js(self, arg)`: Jumps to the specified address if the sign flag is set. 24 | 25 | - `jns(self, arg)`: Jumps to the specified address if the sign flag is not set. 26 | 27 | - `jo(self, arg)`: Jumps to the specified address if the overflow flag is set. 28 | 29 | - `jno(self, arg)`: Jumps to the specified address if the overflow flag is not set. 30 | 31 | - `mov(self, op, args)`: Moves data between registers and memory. 32 | 33 | - `cmp(self, op, args)`: Compares two operands and sets the status register based on the result. 34 | 35 | - `push(self, stack, arg)`: Pushes the operand onto the specified stack. 36 | 37 | - `pop(self, stack)`: Pops the top value from the specified stack. 38 | 39 | - `out(self, op, arg)`: Outputs the value of the operand. 40 | 41 | - `asm_in(self, op, arg)`: Inputs a value into the operand. 42 | 43 | - `swap(self, arg)`: Swaps the values of two operands. 44 | 45 | - `inc(self, arg)`: Increments the operand by 1. 46 | 47 | - `dec(self, arg)`: Decrements the operand by 1. 48 | 49 | - `fetch(self, loc)`: Fetches the next instruction from memory. 50 | 51 | - `load(self, code, name)`: Loads the machine code into memory. 52 | 53 | - `run(self)`: Executes the loaded machine code. 54 | 55 | ## Examples 56 | 57 | ### Example 1: Basic Usage 58 | 59 | ```python 60 | from QuadShot import CPU 61 | 62 | # Initialize the CPU 63 | cpu = CPU() 64 | 65 | # Load the machine code from a file 66 | with open('example.drip') as file: 67 | cpu.load(file, 'example') 68 | 69 | # Run the machine code 70 | cpu.run() 71 | ``` 72 | 73 | ### Example 2: Input and Output 74 | 75 | ```python 76 | from QuadShot import CPU 77 | 78 | # Initialize the CPU with input 79 | cpu = CPU() 80 | cpu.stdin = ['example input'] 81 | 82 | # Load the machine code from a file 83 | with open('example.drip') as file: 84 | cpu.load(file, 'example') 85 | 86 | # Run the machine code 87 | cpu.run() 88 | 89 | # Get the output 90 | output = ''.join(map(chr, map(int, cpu.stdout))) 91 | print(output) 92 | ``` 93 | 94 | ## Purpose and Benefits 95 | 96 | The runtime environment provides a platform for executing machine code generated by the assembler. It manages the CPU, memory, and input/output operations, allowing for the execution of complex programs. The runtime environment also provides a way to interact with the program through input and output buffers, making it possible to run programs that require user input or produce output. 97 | -------------------------------------------------------------------------------- /registers.py: -------------------------------------------------------------------------------- 1 | # register API class file 2 | # TODO: implement [ABCD][HL][HL] 3 | from Drip import registers 4 | registers = list(registers.values()) 5 | mask = {'000_': 'l', '00_0': 'h', '0_00': 'x', '_000': 'a'} 6 | 7 | 8 | def build(reg): 9 | r = reg.replace('0', '') 10 | slot = mask[reg.replace(r, '_')] 11 | return r, slot 12 | 13 | 14 | class Single: 15 | def __init__(self): 16 | # super(Single, self).__init__() 17 | self.val = '0000' 18 | 19 | def __add__(self, other): 20 | return self.val + str(other) 21 | 22 | def assign(self): 23 | return self 24 | 25 | def get(self): 26 | return self.val 27 | 28 | def set(self, val): 29 | self.val = val 30 | 31 | 32 | class Double: 33 | def __init__(self, l: Single, h: Single): 34 | self.l, self.h = l, h 35 | 36 | def get(self): 37 | return self.h.get() + self.l.get() 38 | 39 | def set(self, value): 40 | value = value.zfill(8) 41 | self.h.set(value[:4]) 42 | self.l.set(value[4:]) 43 | 44 | def assign(self): 45 | return self 46 | 47 | 48 | class Adjacent: 49 | def __init__(self, y: Double, z: Double): 50 | self.y, self.z = y, z 51 | 52 | def get(self): 53 | return self.y.get() + self.z.get() 54 | 55 | def set(self, value): 56 | value = value.zfill(16) 57 | self.y.set(value[:8]) 58 | self.z.set(value[8:]) 59 | 60 | def assign(self): 61 | return self 62 | 63 | 64 | def build_register(): 65 | l, h = Single(), Single() 66 | x = Double(l, h) 67 | loc = locals() 68 | return tuple([loc[inst].assign() for inst in ['x', 'h', 'l']]) 69 | 70 | 71 | class Register(dict): 72 | def __init__(self): 73 | dict.__init__(self, dict(zip(['x', 'h', 'l'], build_register()))) 74 | 75 | def add_adjacent(self, adj): 76 | dict.__setitem__(self, 'a', adj) 77 | 78 | def __setitem__(self, key, value): 79 | dict.__getitem__(self, key).set(value) 80 | 81 | def __getitem__(self, item): 82 | return dict.__getitem__(self, item).get() 83 | 84 | def assign(self, item): 85 | return dict.__getitem__(self, item).assign() 86 | 87 | 88 | class Registers(dict): 89 | def __init__(self): 90 | dict.__init__(self) 91 | dict.update(self, dict(zip([str(n) for n in range(1,5)], [Register() for _ in range(12)]))) 92 | doubles = [dict.__getitem__(self, k).assign('x') for k in [str(n) for n in range(1, 5)]] 93 | doubles.append(doubles[0]) 94 | adjacents = [Adjacent(doubles[i], doubles[i + 1]) for i in range(4)] 95 | for i in range(1, 5): 96 | dict.__getitem__(self, str(i)).add_adjacent(adjacents[i - 1]) 97 | 98 | def __getitem__(self, item): 99 | reg, slot = build(item) 100 | return dict.__getitem__(self, reg)[slot] 101 | 102 | def __setitem__(self, key, value): 103 | reg, slot = build(key) 104 | dict.__getitem__(self, reg)[slot] = value 105 | 106 | 107 | if __name__ == '__main__': 108 | r = Registers() 109 | r['0100'] = '12345678' 110 | r['0020'] = 'FFFF' 111 | r['0003'] = '1111' 112 | for i in ['0100', '0200', '0300', '1000', '2000', '3000', '4000']: 113 | print(i, r[i]) 114 | -------------------------------------------------------------------------------- /docs/isa.md: -------------------------------------------------------------------------------- 1 | # Instruction Set Architecture (ISA) 2 | 3 | ## Overview 4 | 5 | The instruction set architecture (ISA) defines the set of instructions that the CPU can execute. These instructions are used to perform various operations such as arithmetic, data movement, comparison, and control flow. The ISA is implemented in the `Drip.py` and `QuadShot.py` files. 6 | 7 | ## Instruction Formats 8 | 9 | Each instruction consists of an opcode and operands. The opcode specifies the operation to be performed, and the operands specify the data to be operated on. The format of an instruction is as follows: 10 | 11 | ``` 12 | [opcode] [operand1], [operand2] 13 | ``` 14 | 15 | ### Arithmetic Instructions 16 | 17 | - **ADD**: Adds two operands. 18 | - Format: `ADD [destination], [source]` 19 | - Example: `ADD AX, BX` 20 | 21 | - **SUB**: Subtracts the second operand from the first operand. 22 | - Format: `SUB [destination], [source]` 23 | - Example: `SUB AX, BX` 24 | 25 | - **MUL**: Multiplies two operands. 26 | - Format: `MUL [destination], [source]` 27 | - Example: `MUL AX, BX` 28 | 29 | - **DIV**: Divides the first operand by the second operand. 30 | - Format: `DIV [destination], [source]` 31 | - Example: `DIV AX, BX` 32 | 33 | - **MOD**: Computes the remainder of the division of the first operand by the second operand. 34 | - Format: `MOD [destination], [source]` 35 | - Example: `MOD AX, BX` 36 | 37 | - **XOR**: Performs a bitwise XOR operation on two operands. 38 | - Format: `XOR [destination], [source]` 39 | - Example: `XOR AX, BX` 40 | 41 | - **INC**: Increments the operand by 1. 42 | - Format: `INC [operand]` 43 | - Example: `INC AX` 44 | 45 | - **DEC**: Decrements the operand by 1. 46 | - Format: `DEC [operand]` 47 | - Example: `DEC AX` 48 | 49 | ### Data Movement Instructions 50 | 51 | - **MOV**: Moves data from the source operand to the destination operand. 52 | - Format: `MOV [destination], [source]` 53 | - Example: `MOV AX, BX` 54 | 55 | - **PUSH**: Pushes the operand onto the stack. 56 | - Format: `PUSH [operand]` 57 | - Example: `PUSH AX` 58 | 59 | - **POP**: Pops the top value from the stack into the operand. 60 | - Format: `POP [operand]` 61 | - Example: `POP AX` 62 | 63 | - **PUSHG**: Pushes the operand onto the global stack. 64 | - Format: `PUSHG [operand]` 65 | - Example: `PUSHG AX` 66 | 67 | - **POPG**: Pops the top value from the global stack into the operand. 68 | - Format: `POPG [operand]` 69 | - Example: `POPG AX` 70 | 71 | - **SWP**: Swaps the two halves of a register. 72 | - Format: `SWP [operand]` 73 | - Example: `SWP AX` 74 | 75 | ### Comparison Instructions 76 | 77 | - **CMP**: Compares two operands and sets the status register based on the result. 78 | - Format: `CMP [operand1], [operand2]` 79 | - Example: `CMP AX, BX` 80 | 81 | ### Control Flow Instructions 82 | 83 | - **JMP**: Jumps to the specified address. 84 | - Format: `JMP [address]` 85 | - Example: `JMP 0x1000` 86 | 87 | - **JZ**: Jumps to the specified address if the zero flag is set. 88 | - Format: `JZ [address]` 89 | - Example: `JZ 0x1000` 90 | 91 | - **JNZ**: Jumps to the specified address if the zero flag is not set. 92 | - Format: `JNZ [address]` 93 | - Example: `JNZ 0x1000` 94 | 95 | - **JS**: Jumps to the specified address if the sign flag is set. 96 | - Format: `JS [address]` 97 | - Example: `JS 0x1000` 98 | 99 | - **JNS**: Jumps to the specified address if the sign flag is not set. 100 | - Format: `JNS [address]` 101 | - Example: `JNS 0x1000` 102 | 103 | - **JO**: Jumps to the specified address if the overflow flag is set. 104 | - Format: `JO [address]` 105 | - Example: `JO 0x1000` 106 | 107 | - **JNO**: Jumps to the specified address if the overflow flag is not set. 108 | - Format: `JNO [address]` 109 | - Example: `JNO 0x1000` 110 | 111 | - **CALL**: Calls a subroutine at the specified address. 112 | - Format: `CALL [address]` 113 | - Example: `CALL 0x1000` 114 | 115 | - **RET**: Returns from a subroutine. 116 | - Format: `RET` 117 | - Example: `RET` 118 | 119 | ### Input/Output Instructions 120 | 121 | - **OUT**: Outputs the value of the operand. 122 | - Format: `OUT [operand]` 123 | - Example: `OUT AX` 124 | 125 | - **IN**: Inputs a value into the operand. 126 | - Format: `IN [operand]` 127 | - Example: `IN AX` 128 | 129 | ### Subroutine Definition 130 | 131 | - **.sub**: Defines the start of a subroutine. 132 | - Format: `.sub([subroutine_name])` 133 | - Example: `.sub(PRINT)` 134 | 135 | - **.endsub**: Defines the end of a subroutine. 136 | - Format: `.endsub` 137 | - Example: `.endsub` 138 | 139 | ## Preprocessing 140 | 141 | Before the assembly code is passed to the assembler, a preprocessing step is performed. During this step, subroutine names are replaced with their corresponding addresses. This allows the `CALL` instruction to use subroutine names, which are then replaced with the correct addresses during preprocessing. 142 | 143 | ## Examples 144 | 145 | ### Example 1: Addition 146 | 147 | ``` 148 | MOV AX, 5 149 | MOV BX, 10 150 | ADD AX, BX 151 | ``` 152 | 153 | This example moves the value 5 into the AX register, the value 10 into the BX register, and then adds the values in AX and BX, storing the result in AX. 154 | 155 | ### Example 2: Loop 156 | 157 | ``` 158 | MOV CX, 10 159 | LOOP_START: 160 | DEC CX 161 | JNZ LOOP_START 162 | ``` 163 | 164 | This example initializes the CX register to 10, then enters a loop that decrements CX and jumps back to the start of the loop if CX is not zero. 165 | 166 | ### Example 3: Subroutine Call 167 | 168 | ``` 169 | CALL SUBROUTINE 170 | ... 171 | SUBROUTINE: 172 | MOV AX, 1 173 | RET 174 | ``` 175 | 176 | This example calls a subroutine that moves the value 1 into the AX register and then returns to the caller. 177 | 178 | ### Example 4: Subroutine Definition and Call 179 | 180 | ``` 181 | .sub(PRINT): 182 | OUT AX 183 | RET 184 | .endsub 185 | 186 | MOV AX, 5 187 | CALL PRINT 188 | ``` 189 | 190 | This example defines a subroutine named `PRINT` that outputs the value in the AX register and then returns. The main program moves the value 5 into the AX register and calls the `PRINT` subroutine. 191 | 192 | ## Subroutine Definition 193 | 194 | Subroutines are defined using the `.sub` directive and terminated with the `.endsub` directive. The `.sub` directive takes the name of the subroutine as an argument. The subroutine code is placed between the `.sub` and `.endsub` directives. 195 | 196 | ### Example 197 | 198 | ``` 199 | .sub(PRINT): 200 | OUT AX 201 | RET 202 | .endsub 203 | ``` 204 | 205 | In this example, a subroutine named `PRINT` is defined. The subroutine outputs the value in the AX register and then returns. 206 | 207 | ## Calling Subroutines 208 | 209 | Subroutines are called using the `CALL` instruction. The `CALL` instruction takes the name of the subroutine as an argument. The `make_callable` function in `Drip.py` processes subroutine calls by replacing the `CALL` instruction with the subroutine's address. 210 | 211 | ### Example 212 | 213 | ``` 214 | MOV AX, 5 215 | CALL PRINT 216 | ``` 217 | 218 | In this example, the value 5 is moved into the AX register, and then the `PRINT` subroutine is called. 219 | 220 | ## Returning from Subroutines 221 | 222 | Subroutines return to the caller using the `RET` instruction. The `RET` instruction does not take any arguments. 223 | 224 | ### Example 225 | 226 | ``` 227 | .sub(PRINT): 228 | OUT AX 229 | RET 230 | .endsub 231 | ``` 232 | 233 | In this example, the `PRINT` subroutine outputs the value in the AX register and then returns to the caller. 234 | -------------------------------------------------------------------------------- /QuadShot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from operator import add, sub, mul, mod, floordiv, xor 4 | from functools import partial 5 | from registers import Registers 6 | import sys 7 | 8 | import ram 9 | import Drip 10 | 11 | 12 | def twos_comp(val, bits): 13 | """compute the 2's compliment of int value val""" 14 | if val & (1 << (bits - 1)) != 0: 15 | val -= (1 << bits) 16 | return val 17 | 18 | 19 | def sign(a): 20 | if a < 0: 21 | return -1 22 | elif a > 0: 23 | return 1 24 | else: 25 | return 0 26 | 27 | 28 | def cmp(a, b): 29 | return sign(a - b) 30 | 31 | 32 | class CPU: 33 | def __init__(self): 34 | self.registers = Registers() # dict.fromkeys(Drip.registers.values(), '0000') 35 | self.IP = 0 36 | self.init = 0 37 | self.CSP = int('0xFFFF', 16) 38 | self.GSP = int('0xFEFF', 16) 39 | self.MSP = int('0xFDFF', 16) 40 | self.PSP = self.MSP 41 | self.SR = [0, 0, 0, 0, 0, 0, 0, 0] 42 | self.jumped = 0 43 | self.ram = ram.RAM() 44 | self.stdout = [] 45 | self.stdin = [Drip.tohex(ord(c)) for c in sys.argv[-1]] + ['000A'] if len(sys.argv) > 2 else ['000A'] 46 | print(self.stdin) 47 | self.stack = [] 48 | self.debug = '' 49 | self.table = { 50 | '00A0': {'len': 2, 'op': add}, 51 | '00A1': {'len': 2, 'op': sub}, 52 | '00A2': {'len': 2, 'op': mul}, 53 | '00A3': {'len': 2, 'op': floordiv}, 54 | '00A4': {'len': 1, 'op': self.inc}, 55 | '00A5': {'len': 1, 'op': self.dec}, 56 | '00A6': {'len': 2, 'op': mod}, 57 | '00A7': {'len': 2, 'op': xor}, 58 | '00B0': {'len': 2, 'op': add}, 59 | '00B1': {'len': 2, 'op': sub}, 60 | '00B2': {'len': 2, 'op': mul}, 61 | '00B3': {'len': 2, 'op': floordiv}, 62 | '00B6': {'len': 2, 'op': mod}, 63 | '00B7': {'len': 2, 'op': xor}, 64 | '00C0': {'len': 1, 'op': self.jmp}, 65 | '00C1': {'len': 1, 'op': self.jz}, 66 | '00C2': {'len': 1, 'op': self.jnz}, 67 | '00C3': {'len': 1, 'op': self.js}, 68 | '00C4': {'len': 1, 'op': self.jns}, 69 | '00C5': {'len': 1, 'op': self.jo}, 70 | '00C6': {'len': 1, 'op': self.jno}, 71 | '00D0': {'len': 2, 'op': self.mov}, 72 | '00D1': {'len': 2, 'op': self.mov}, 73 | '00D2': {'len': 2, 'op': self.mov}, 74 | '00D3': {'len': 2, 'op': self.mov}, 75 | '00D4': {'len': 2, 'op': self.mov}, 76 | '00DA': {'len': 2, 'op': self.cmp}, 77 | '00DB': {'len': 2, 'op': self.cmp}, 78 | '00DC': {'len': 2, 'op': self.cmp}, 79 | '00DD': {'len': 2, 'op': self.cmp}, 80 | '00E0': {'len': 1, 'op': self.push}, 81 | '00E1': {'len': 1, 'op': self.pop}, 82 | '00E2': {'len': 1, 'op': self.push}, 83 | '00E3': {'len': 1, 'op': self.pop}, 84 | '00EA': {'len': 1, 'op': self.push}, 85 | '00EB': {'len': 1, 'op': self.pop}, 86 | '00EC': {'len': 1, 'op': self.push}, 87 | '00ED': {'len': 1, 'op': self.pop}, 88 | '00F0': {'len': 1, 'op': self.out}, 89 | '00F1': {'len': 1, 'op': self.out}, 90 | '00F2': {'len': 1, 'op': self.out}, 91 | '00FD': {'len': 1, 'op': self.asm_in}, 92 | '00FE': {'len': 1, 'op': self.asm_in}, 93 | '00FF': {'len': 1, 'op': self.asm_in}, 94 | '0A00': {'len': 1, 'op': None}, 95 | '0B00': {'len': 1, 'op': None}, 96 | '0C00': {'len': 1, 'op': self.swap}, 97 | '0000': {'len': 0, 'op': 'HALT'} 98 | } 99 | 100 | def math(self, func, args): 101 | a = args[0] 102 | if func == self.inc: 103 | return self.inc(args) 104 | elif func == self.dec: 105 | return self.dec(args) 106 | args[0] = func(*args) 107 | if args[0] > (2**16)-1: 108 | args[0] = mod(args[0], (2**16)-1) 109 | self.SR = 4 110 | if cmp(a, args[0]) == 0: 111 | self.SR = 2 112 | elif cmp(a, args[0]) == -1: 113 | self.SR = 8 114 | return args[0] 115 | 116 | def jmp(self, arg): 117 | jump = twos_comp(int(arg[0], 16), 16) 118 | # print('Added', jump, 'to IP') 119 | self.IP += jump 120 | self.jumped = 1 121 | 122 | def jz(self, arg): 123 | if self.SR == 2: 124 | self.jmp(arg) 125 | 126 | def jnz(self, arg): 127 | if self.SR != 2: 128 | self.jmp(arg) 129 | 130 | def js(self, arg): 131 | if self.SR == 8: 132 | self.jmp(arg) 133 | 134 | def jns(self, arg): 135 | if self.SR != 8: 136 | self.jmp(arg) 137 | 138 | def jo(self, arg): 139 | if self.SR == 4: 140 | self.jmp(arg) 141 | 142 | def jno(self, arg): 143 | if self.SR != 4: 144 | self.jmp(arg) 145 | 146 | def mov(self, op: str, args: list): 147 | if op == '00D0': 148 | self.registers[args[0]] = args[1] 149 | elif op == '00D1': 150 | self.registers[args[0]] = self.ram.get(args[1]) 151 | elif op == '00D2': 152 | self.ram.put(args[0], self.registers[args[1]]) 153 | elif op == '00D3': 154 | self.registers[args[0]] = self.ram.get(self.registers[args[1]]) 155 | elif op == '00D4': 156 | self.ram.put(self.registers[args[0]], self.registers[args[1]]) 157 | 158 | def cmp(self, op: str, args: list): 159 | self.SR = 0 160 | if op == '00DA': 161 | args = [self.registers[args[0]], self.registers[args[1]]] 162 | elif op == '00DB': 163 | args = [self.registers[args[0]], args[1]] 164 | elif op == '00DC': 165 | args = [self.registers[args[0]], self.ram.get(args[1])] 166 | elif op == '00DD': 167 | args = [self.ram.get(self.registers[args[0]]), args[1]] 168 | res = cmp(*map(partial(int, base=16), args)) 169 | if res == 0: 170 | self.SR = 2 171 | elif res == -1: 172 | self.SR = 8 173 | 174 | def push(self, stack, arg): 175 | if stack == 'call': 176 | self.ram.put(Drip.tohex(self.CSP), arg) 177 | self.CSP -= 1 178 | elif stack == 'global': 179 | self.ram.put(Drip.tohex(self.GSP), arg) 180 | self.GSP -= 1 181 | elif stack == 'private': 182 | self.ram.put(Drip.tohex(self.PSP), arg) 183 | self.PSP -= 1 184 | 185 | def pop(self, stack): 186 | if stack == 'call': 187 | self.CSP += 1 188 | return self.ram.get(Drip.tohex(self.CSP)) 189 | elif stack == 'global': 190 | self.GSP += 1 191 | return self.ram.get(Drip.tohex(self.GSP)) 192 | elif stack == 'private': 193 | self.PSP += 1 194 | return self.ram.get(Drip.tohex(self.PSP)) 195 | 196 | def out(self, op, arg): 197 | if op[3] == '0': # Literal 198 | self.stdout.append(arg[0]) 199 | elif op[3] == '1': # Register 200 | self.stdout.append(self.registers[arg[0]]) 201 | else: # RAM 202 | self.stdout.append(self.ram.get(self.registers[arg[0]])) 203 | 204 | def asm_in(self, op, arg): 205 | if op[3] == 'F': # Register 206 | self.registers[arg[0]] = self.stdin.pop(0) 207 | elif op[3] == 'E': # RAM (Register) 208 | self.ram.put(self.registers[arg[0]], self.stdin.pop(0)) 209 | else: # RAM (Direct) 210 | self.ram.put(self.ram.get(arg[0]), self.stdin.pop(0)) 211 | 212 | def swap(self, arg): 213 | data = self.registers[arg[0]] 214 | half = int(len(data) / 2) 215 | self.registers[arg[0]] = data[half:] + data[:half] 216 | 217 | @staticmethod 218 | def inc(arg): 219 | return arg[0] + 1 220 | 221 | @staticmethod 222 | def dec(arg): 223 | return arg[0] - 1 224 | 225 | def fetch(self, loc): 226 | op = self.ram.get(loc) 227 | lookup = self.table[op] 228 | func, forward = lookup['op'], lookup['len'] 229 | args = [self.ram.get(hex(int(loc, 16) + i + 1)) for i in range(forward)] 230 | return func, args, forward, op 231 | 232 | def load(self, code, name): 233 | loaded = Drip.load(code, name) 234 | self.ram.image, self.init = loaded[0].image, loaded[1] 235 | 236 | def run(self): 237 | self.registers = Registers() 238 | self.IP = 0 + self.init 239 | print('init', self.init) 240 | self.CSP = int('0xFFFF', 16) 241 | self.GSP = int('0xFEFF', 16) 242 | self.MSP = int('0xFDFF', 16) 243 | self.SR = 0 244 | self.debug = '' 245 | while True: 246 | self.jumped = 0 247 | func, args, forward, op = self.fetch(hex(self.IP)) 248 | if func == 'HALT': 249 | break 250 | elif op[2] == 'A': 251 | self.registers[args[0]] = Drip.tohex( 252 | self.math(func, [int(self.registers[register], 16) for register in args])) 253 | elif op[2] == 'B': 254 | self.registers[args[0]] = Drip.tohex( 255 | self.math(func, [int(self.registers[args[0]], 16), int(args[1], 16)])) 256 | elif op[2] == 'D' or op[2] == 'F': 257 | func(op, args) 258 | elif op[1] == 'A': 259 | self.push('call', Drip.tohex(self.IP)) 260 | print('call branch') 261 | self.IP = int(args[0], 16) 262 | self.MSP -= 256 263 | self.PSP = self.MSP 264 | continue 265 | elif op[1] == 'B': 266 | self.IP = int(self.pop('call'), 16) 267 | print('return branch') 268 | self.MSP += 256 269 | self.PSP = self.MSP 270 | elif op == '00E0': 271 | self.push('private', self.registers[args[0]]) 272 | elif op == '00E1': 273 | self.registers[args[0]] = self.pop('private') 274 | elif op == '00EA': 275 | self.push('global', self.registers[args[0]]) 276 | elif op == '00EB': 277 | self.registers[args[0]] = self.pop('global') 278 | else: 279 | func(args) 280 | if not self.jumped: 281 | self.IP += forward + 1 282 | print('out', self.stdout) 283 | print(''.join(map(chr, map(partial(int, base=16), self.stdout)))) 284 | print('Finished') 285 | 286 | 287 | if __name__ == '__main__': 288 | test = CPU() 289 | if len(sys.argv) < 2: 290 | name = 'BUBBLE2' 291 | else: 292 | name = sys.argv[1] 293 | with open(name + '.drip') as file: 294 | test.load(file, name) 295 | test.run() 296 | -------------------------------------------------------------------------------- /Drip.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import ram 4 | import Grind 5 | 6 | 7 | jumps = {'JMP': '00C0', 8 | 'JZ': '00C1', 9 | 'JNZ': '00C2', 10 | 'JS': '00C3', 11 | 'JNS': '00C4', 12 | 'JO': '00C5', 13 | 'JNO': '00C6'} 14 | table = { 15 | 'arithmetic': { 16 | 'ADD': ['00A0', '00B0'], 17 | 'SUB': ['00A1', '00B1'], 18 | 'MUL': ['00A2', '00B2'], 19 | 'DIV': ['00A3', '00B3'], 20 | 'MOD': ['00A6', '00B6'], 21 | 'XOR': ['00A7', '00B7']}, 22 | 'INC': '00A4', 23 | 'DEC': '00A5', 24 | 'jumps': jumps, 25 | 'MOV': ['00D0', '00D1', '00D2', '00D3', '00D4'], 26 | 'CMP': ['00DC', '00DA', '00DB', '00DD'], 27 | 'PUSH': '00E0', 28 | 'POP': '00E1', 29 | 'PUSHG': '00EA', 30 | 'POPG': '00EB', 31 | 'OUT': ['00F0', '00F1', '00F2'], 32 | 'IN': ['00FF', '00FE', '00FD'], 33 | 'CALL': '0A00', 34 | 'RET': '0B00', 35 | 'SWP': '0C00' 36 | } 37 | 38 | 39 | registers = {'AH': '0010', 40 | 'AL': '0001', 41 | 'AX': '0100', 42 | 'BH': '0020', 43 | 'BL': '0002', 44 | 'BX': '0200', 45 | 'CH': '0030', 46 | 'CL': '0003', 47 | 'CX': '0300', 48 | 'DH': '0040', 49 | 'DL': '0004', 50 | 'DX': '0400', 51 | 'ABX': '1000', 52 | 'BCX': '2000', 53 | 'CDX': '3000', 54 | 'DAX': '4000' 55 | } 56 | 57 | data_table = { 58 | 'global': 0, 59 | 'private': 1 60 | } 61 | 62 | d = [] 63 | 64 | 65 | def data(line): 66 | t, var = line.split('>') 67 | var, d = var.split('=') 68 | if t == 'literal': 69 | d = [d] 70 | elif t == 'list': 71 | d = list(d[1:-1].split(',')) 72 | elif t == 'string': 73 | d = [tohex(ord(v)) for v in d[1:-1]] + ['000A'] 74 | return var, d 75 | 76 | 77 | def tokenize(lines): 78 | mode = 0 79 | for line in lines: 80 | op, args = None, None 81 | line = line.strip('\n') 82 | 83 | if '.data_' in line: 84 | mode = 1 85 | continue 86 | if '.exec_' in line: 87 | mode = 2 88 | continue 89 | if '%' in line: 90 | continue 91 | 92 | match mode: 93 | case 1: 94 | d.append(data(line.strip('\t').strip(' '))) 95 | continue 96 | case 2: 97 | if line.strip('\t').strip(' ')[0] == ';': 98 | continue 99 | token = line.split(';')[0].strip('\t').split('|')[0] 100 | match token: 101 | case t if ':' in t and t != '.': 102 | yield t.strip('\t').strip(' '), [] 103 | continue 104 | case t if 'END' in t: 105 | yield 'END', [] 106 | continue 107 | case t if t.startswith('.'): 108 | yield t.strip('\t').strip(' '), [] 109 | continue 110 | case _: 111 | op, args = token.split()[0], token.split()[1].split(',') 112 | case _: 113 | pass 114 | 115 | yield op, args 116 | 117 | 118 | def parse(lines): 119 | is_mem = lambda arg: arg[0] == '[' and arg[-1] == ']' 120 | for line in lines: 121 | op, args = line 122 | 123 | if args: 124 | match op: 125 | case 'DB': 126 | if len(args[0]) == 4: 127 | op, args = args, [] 128 | else: 129 | args = [tohex(ord(n)) for n in args[0][1:-1]] 130 | op, args = args[0], args[1:] 131 | 132 | case 'CMP': 133 | if is_mem(args[1]): 134 | op = table[op][0] 135 | args[0] = registers[args[0]] 136 | args[1] = args[1][1:-1] 137 | elif args[1] in registers: 138 | op = table[op][1] 139 | args = [registers[a] for a in args] 140 | elif is_mem(args[0]): 141 | op = table[op][3] 142 | args[0] = registers[args[0][1:-1]] 143 | else: 144 | op = table[op][2] 145 | args[0] = registers[args[0]] 146 | 147 | case cmd if cmd in table['arithmetic']: 148 | if args[1] in registers: 149 | op = table['arithmetic'][op][0] 150 | args = [registers[a] for a in args] 151 | else: 152 | op = table['arithmetic'][op][1] 153 | args[0] = registers[args[0]] 154 | 155 | case cmd if cmd in table['jumps']: 156 | op = table['jumps'][op] 157 | 158 | case 'MOV': 159 | op, args = mov(args) 160 | 161 | case _ if len(args) == 1 and op in table: 162 | match op: 163 | case 'OUT': 164 | if is_mem(args[0]): 165 | op = table[op][2] 166 | args[0] = registers[args[0][1:-1]] 167 | elif args[0] in registers: 168 | op = table[op][1] 169 | args[0] = registers[args[0]] 170 | else: 171 | op = table[op][0] 172 | case 'IN': 173 | if args[0] in registers: 174 | op = table[op][0] 175 | args[0] = registers[args[0]] 176 | elif is_mem(args[0]): 177 | if args[0][1:-1] in registers: 178 | op = table[op][1] 179 | args[0] = registers[args[0][1:-1]] 180 | else: 181 | op = table[op][2] 182 | args[0] = args[0][1:-1] 183 | case 'CALL' | 'RET': 184 | op, args = table[op], args 185 | case _: 186 | if args[0] in registers: 187 | op = table[op] 188 | args = [registers[args[0]]] 189 | else: 190 | op = table[op] 191 | 192 | case _: 193 | pass 194 | 195 | else: 196 | match op: 197 | case 'OUT': 198 | if args[0][0] == '[' and args[0][-1] == ']': 199 | op = table[op][2] 200 | args[0] = args[0][1:-1] 201 | elif args[0] in registers: 202 | op = table[op][1] 203 | args[0] = registers[args[0]] 204 | else: 205 | op = table[op][0] 206 | case 'END': 207 | op, args = '0000', [] 208 | case _: 209 | pass 210 | 211 | yield op, args 212 | 213 | 214 | def mov(args): 215 | match args[0] in registers: 216 | case True: 217 | match len(args[1]): 218 | case 4 if args[1][1:-1] in registers: 219 | return '00D3', [registers[args[0]], registers[args[1][1:-1]]] 220 | case 6: 221 | return '00D1', [registers[args[0]], args[1][1:-1]] 222 | case _: 223 | return '00D0', [registers[args[0]], args[1]] 224 | case False: 225 | match args[0][1:-1] in registers: 226 | case True: 227 | return '00D4', [registers[args[0][1:-1]], registers[args[1]]] 228 | case False: 229 | return '00D2', [args[0][1:-1], registers[args[1]]] 230 | 231 | 232 | def flatten(foo): 233 | for x in foo: 234 | if hasattr(x, '__iter__') and not isinstance(x, str): 235 | for y in flatten(x): 236 | yield y 237 | else: 238 | yield x 239 | 240 | 241 | def tohex(val): 242 | return hex(val & 0xFFFF)[2:].zfill(4).upper() 243 | 244 | 245 | def twos_comp(val, bits): 246 | """compute the 2's compliment of int value val""" 247 | if val & (1 << (bits - 1)) != 0: 248 | val -= (1 << bits) 249 | return val 250 | 251 | 252 | def find_subs(program: list): 253 | sub_found = False 254 | subs = {} 255 | sub = [] 256 | main = [] 257 | name = '' 258 | for lineno in range(len(program)): 259 | if '.sub(' in program[lineno][0]: 260 | name = program[lineno][0][5:-2] 261 | sub = [] 262 | sub_found = True 263 | sub.append((program[lineno][0][5:-2] + ':', [])) 264 | elif sub_found and program[lineno][0] != '.endsub:': 265 | sub.append(program[lineno]) 266 | elif program[lineno][0] == '.endsub:': 267 | subs[name] = sub 268 | sub_found = False 269 | else: 270 | main.append(program[lineno]) 271 | return main, subs 272 | 273 | 274 | def find_jumps(program): 275 | matches = {} 276 | l = program[:] 277 | for item in l: 278 | if item.endswith(':'): 279 | matches[item[:-1]] = l.index(item) 280 | l.remove(item) 281 | for i in range(len(l)): 282 | if l[i] in jumps.values(): 283 | if l[i + 1] in matches.keys(): 284 | print('Jump to', l[i + 1], end='') 285 | l[i + 1] = tohex(matches[l[i + 1]] - i) 286 | print(' =', twos_comp(int(l[i + 1], 16), 16)) 287 | return l 288 | 289 | 290 | def make_callable(program, calls): 291 | call = False 292 | out = [] 293 | count = 0 294 | for i in range(len(program)): 295 | if program[i] == '0A00': 296 | call = True 297 | continue 298 | elif call: 299 | out.append(calls[program[i]]) 300 | program[i] = out[-1] 301 | count += 1 302 | call = False 303 | return program, out 304 | 305 | 306 | def load(program, fname): 307 | memory = ram.RAM() 308 | settings = None 309 | if fname + '.dripc' in os.listdir('.'): 310 | grind = Grind.Grind(file=fname + '.drip', config=fname + '.dripc') 311 | print(grind.subs, grind.switches) 312 | grind.sub() 313 | program = grind.out() 314 | if grind.switches: 315 | settings = grind.switches 316 | program = list(tokenize(program)) 317 | var_table = {} 318 | for var in d: 319 | name, val = var 320 | var_table[name] = val 321 | program, subs = find_subs(program) 322 | subs2 = [] 323 | sub_locs = {} 324 | sub_loc = 0 325 | for sub in subs: 326 | subs2.append(find_jumps(list(flatten(parse(subs[sub]))))) 327 | sub_locs[sub] = tohex(sub_loc) 328 | sub_loc += len(subs2[-1]) 329 | subs = subs2 330 | del subs2 331 | program, call_table, = make_callable(find_jumps(list(flatten(parse(program)))), sub_locs) 332 | sub_list = list(flatten(subs)) 333 | program = sub_list + program 334 | base = int('FF00', 16) 335 | var_table_ref = {} 336 | address = int('FE00', 16) 337 | for varname in var_table: 338 | save = address 339 | var_table_ref[varname] = address 340 | print(varname, '-->', var_table[varname]) 341 | for item in var_table[varname]: 342 | memory.put(tohex(address), item) 343 | address += 1 344 | address = save - 256 345 | final = program[:] 346 | for i in range(len(final)): 347 | if final[i][0] == '[': 348 | final[i] = final[i][1:-1] 349 | if final[i] in var_table_ref: 350 | final[i] = tohex(var_table_ref[final[i]]) 351 | for i in range(len(final)): 352 | memory.put(tohex(i), final[i]) 353 | for address, call in enumerate(call_table): 354 | memory.put(tohex(base + address), call) 355 | memory.show() 356 | return memory, len(list(flatten(subs))) 357 | --------------------------------------------------------------------------------