AttoASM

From Esolang
Jump to navigation Jump to search
AttoWPU logo

AttoASM is a language designed for the AttoWPU experimental processing unit, as part of the WPU (Weird Processing Unit) concept. AttoASM is the assembly language of the AttoWPU unit and thus corresponds to the basic operation of this architecture. Both AttoWPU and AttoASM use very minimalistic design and support only three elementary instructions that change individual bits on a bus. By a series of these elementary operations, various internal units can be controlled to perform various tasks.

WPU

AttoASM and AttoWPU are part of the WPU concept. WPU stands for Weird Processing Unit, which is basically series of various experimental processors (architectures) that try to be interesting, new, original, extraordinary, different or just plain weird. WPUs try to be original and playful and they usually go beyond the boundaries of convention - things we consider normal.

WPU can be any processor, that has at least part of it designed in a weird, unusual way, that makes programming for it and the way the machine code is executed challenging and/or interesting. It doesn't have to be practical in any way, as some of the architectures are more of a "hey, let's try this and see what happens" philosophy - purely experimental, whacky and weird for the sake of fun and curiosity, however sometimes there springs something really useful from all this that can be actually used for some improvement.

AttoWPU

AttoWPU simulator running an example code written in AttoASM

While WPU is common name for any processing unit, attoWPU is a specific WPU and not just that: it's the first WPU created as the start of the series of WPUs, so it's sort of the gateway into the world of the weird processing units. It's certainly not the best of them, or most original of WPUs, but it's a start.

AttoWPU is inspired by the microcode of normal processors and basically builds on the idea, that we can divide processors into two logical parts: execution unit (everything that does various calculations and operations) and control unit (part, that decodes instruction opcodes and tells execution unit what to do). AttoWPU has the control unit reduced to absurd minimum, requiring programmer to basically create a code, that will control the processor's function using three elementary instructions (attoinstructions), each one changing always only one bit. Regarding the execution units, AttoWPU has many of them and they do a lot of job by themselves, to make the programming simpler.

This basically allows you to either create conventional (or not) software using the attoasembly language used to control the parts of the processor, making it a form of hardcore programming, or to use the attoassembly to define a processor's function and let it process higher level instructions of your design. There's also another peculiar perk: because the attocode memory is read/write, it's theoretically possible to create self modifying processor.

There's also another task, related to AttoWPU programming: code optimization and compression. AttoWPU machine code provides a lot of space for program optimization (both making it smaller and run faster, these two are actually equal to some point) and compression (a lot of redudancy), so it's possible to test programming skills in various challenges and competitions.

AttoWPU logical scheme

In short, AttoWPU has the AttoCore, which is capable of processing only elementary instructions, called attoinstructions. AttoWPU has one 64 bit bus, which is divided into four logical buses: address, control, data and Quick aJump bus, each attoinstruction changes always one bit of this bus. There are various units (execution units) connected in parallel to these buses, so programmer must use the buses to instruct them what to do and allow them to exchange data using the data bus. The attoCore is only capable of changing one bit on one of the logical buses at each cycle, everything else must be handled by controlling the units. For better understanding the AttoWPU schematic is recommended.

Thanks the the bus design, the architecture is modular and can be easily modified - new units can be added and removed, while the AttoCore and base operation remains the same. The standard specification provides several units for integer and floating point arithmetic and input and output with easy interface, allowing to quickly get visual feedback in the simulator.

AttoASM

AttoASM is a programming language used to create an attocode - machine code that's processed by the attocore of the AttoWPU. It allows programmer create the attocode by writing individual attoinstructions, but it also provides ways of simplifying the programming and reducing repeating source code. The same thing can't be however said about produced machine code, so if you want to optimize the resulting code, source code will probably be more complex and harder to manage or even create, which can present a certain challenge to programmers.

Simplest Example

2 1 // sets third bit (address 2) 

Hello World Example

Following code presents a Hello World application in AttoASM, that writes the "Hello World!" message on the 40x4 ASCII text display, that's part of the specification.

/* Hello World in AttoASM by Frooxius, slightly  optimized, 5/20/2011, www.attowpu.solirax.org */

EXE { CTRL+7(2) ! }		// execute command

ADDR+4 [01H, 4]		// attocode memory
CTRL+3 [01H, 4]		// write new  address
DATA [TEXT]
EXE

DATA+24 1(8)	// prepare for data exchange

LOOP: 

// cleanup after jump
CTRL+7 0
ADDR+4 [05H, 4]		// out register
CTRL+3 [00H, 4]		// stop the output
EXE

// Write the character
ADDR+4 [01H, 4]		// attocode memory
CTRL+3 [03H, 4]		// output  addressed data
EXE

ADDR+4 [0BH, 4]		// address text display
CTRL+3 [03H, 4]		// write character and increment address
EXE

// Maintain the loop if end of string wasn't reached yet

ADDR+4 [01H, 4]		// address attocode memory
CTRL+3 [07H, 4]		// move to the next element
EXE

ADDR+4 [02H, 4]		// address TEMP register
CTRL+3 [03H, 4]		// write value without mask from the databus
EXE

ADDR+4 [01H, 4]		// attocode memory
CTRL+3 [00H, 4]		// stop data output
EXE

ADDR+4 [04H, 4]		// address ALU
CTRL [29H, 7]		// ZeroSet
DATA [ENDLOOP]
EXE
CTRL [2AH, 7]		// NotZeroSet
DATA [LOOP]
EXE

CTRL 000	// clear three MSB

DATA+24 1(8)	// prepare for data exchange

ADDR+4 [05H, 4]		// OUT register
CTRL+3 [01H, 4]		// output its contents
EXE

ADDR+4 [00H, 4]	// aPC
CTRL+3 [01H, 4]	// write new address from the databus
EXE

ENDLOOP:

// cleanup after jump
CTRL+7 0
ADDR+4 [05H, 4]		// out register
CTRL+3 [00H, 4]		// stop the output
EXE

// infinite loop to stop the program from executing following (nonexistent - gibberish) code
AJMP [INFLOOP, 15]
INFLOOP:
AJMP+15(2) !

text:
"Hello world!" $00

MOV mem, mem Example

/*
  Implementation of MOV mem32, mem32 instruction in the AttoASM
  
  This code is not fully optimized for sake of readibility, it
  can be further optimized to execute faster and also be smaller
  
  Following code assumes, that programmer has already decoded
  addresses of both memory locations and stored them in the
  register memory unit on address 0x03 in registers he called
  TMP0 and TMP1
*/
EXE { CTRL+7(2) ! } // execute the command - Symbol/Macro, equivalent to sequence of CTRL+7 1, CTRL+7 0

CTRL 0(8)           // clear control bus from any previous codes codes
ADDR 0(8)           // clear the address as well
DATA 0(32)          // data as well 

ADDR+4 0011         // address register memory
DATA+27 0001        // move address of the TMP1 register the register memory on the data bus
CTRL+6 1            // prepare command code to control bus, which will tell currently addressed device - spec. data memory to read value on data bus and store it in its own addressation register
CTRL+7 1            // by activating most significant bit on the control bus, tell addressed device to read the instruction code on the control bus and thus address TMP1 register
CTRL+7 0            // don't forget to set it back to zero afterwards
DATA 1(32)          // set all wires of data bus to 1, so it won't interrupt data outputed by special memory
CTRL+5 1            // (in combination with previous CTRL+6 1 forms code 11) prepare instruction code, which will tell special data memory to read value of addressed location and output it on the data bus
CTRL+7 1            // activate control instruction (after this, value of TMP1 will be on the data bus)
CTRL+7 0            // deactivate it again
ADDR+4 0111         // address program/data memory
CTRL+5 01           // prepare command code, which will tell program/data memory to read address from data bus and address appropriate cell (source location)
EXE                 // macro for CTRL+7 1 CTRL+7 0, after this, source location is addressed in program/data memory
CTRL+5 11           // prepare command code, which will tell program/data memory to ouput value at addressed cell to the data bus
EXE                 // value of source address is now at data bus (however, still mixed with the address from TMP1)
ADDR+4 0011         // address special data memory again
CTRL+5 00           // erase previous control instruction, this forms zero instruction, which tells the special data memory to disconnect from data bus (high impedance mode) and thus stop disturbing the value on data bus from source location
EXE
ADDR+4 0010         // address standalone TEMP register
CTRL+5 01           // prepare instruction code which will tell the TEMP register to store value on the data bus in itself
EXE                 // TEMP now contains the source value
ADDR+4 0011         // address register memory again
DATA 0(30)10        // put address of TMP2 register in register memory on the data bus (TMP2 contains target location)
CTRL+5 01           // prepare command code which will tell register memory to address location with address on the data  bus
EXE
CTRL+5 1            // (in combination with previous command code 1 forms code 11) prepare command code, which will tell special data memory to read value of addressed location and output it on the data bus
EXE
ADDR 0111           // address program/data memory
CTRL+5 0            // (clear this bit, changing 11 to 01) prepare instruction code, which will tell program/data memory to read address from data bus and address appropriate cell (target location) and by changing the instruction code, this will also stop it outputing value of source address on the data bus
EXE                 // target location is now addressed
ADDR 0010           // address standalone TEMP again
CTRL+5 10           // prepare instructoin code, which will instruct standalone TEMP to output its value (which is source value) on the data bus
EXE
ADDR 0011           // address register memory
CTRL+6 0            // erase control instruction, to stop special data memory from outputting value on the data bus
EXE                 // data bus now contains only the source value from TEMP
ADDR 0111           // address program/data memory again
CTRL+4 100          // prepare instruction code, which will tell program/data memory to read value from data bus and store it at addressed location
EXE                 // target location now contains value of source location, MOV operation is complete (some cleanup may follow as well as jump to start decoding another instruction from program/data memory)

Syntax Overview

AttoASM supports 4 elementary instructions, which can be written by following symbols:

0      fall
1      rise
!      invert
|      halt (optional)
-      skip (pseudo instruction)

Each statement starts with a number from 0 to 63 which determines the address of the first bit, followed by at least one of the symbols representing the instructions. Each consecutive instruction applies to the following bit of the bus. It's also possible to convert an integer or single precision floating point number to a series of instructions using the square brackets statement.

For simplification, four symbols are predefined, each one holding starting bit of one of the four logical buses:

ADDR = 0     start of the address bus
CTRL = 8     start of the control bus
DATA = 16    start of the data bus
AJMP = 48    start of the Quick aJump bus

Here are some examples of valid statements in AttoASM:

CTRL+2 0001
DATA 110-11001 // is same as:
DATA 110 DATA+4 1101
ADDR+4 !01
DATA [15550]   // write number 15550 to the data bus
ADDR [0CH,8,0]

AttoASM also supports definition of symbols. Symbol basically contains any valid piece of AttoASM code, which is placed at every location, where the symbol is used, as is, with the exception of local symbols inside a symbol, which are made unique for every usage of the symbol by generating an unique name. Symbol can't be used prior its definition, with the exception of labels.

It's also possible to redefine symbol - new definition is valid from the point of redefinition. Redefined version is applied also to any subsequent usages of symbols, that contain usage of the redefined symbol and thus allow to pass arguments or whole pieces of code to the other symbols.

Labels are special form of symbols, when they're defined, they hold an integer number with the address of the instruction immediately following the symbol. They can be used like any other symbol.

Infinite loop example:

EXE { CTRL+7(2) ! } // symbol to execute the command
ADDR 0(8)           // address aPC
CTRL 0(6)01         // prepare command code to write new address to the aPC
DATA+11 [Somelabel, 20]    //write the address of the label to the data bus
Somelabel:
EXE           // execute the command and thus cause jump back to Somelabel

Trivia

The WPU concept was originally concieved by Frooxius, who is now most well known for Neos.

External resources