ok so ive been working on this one project for like 2 years now I think? my fantasy console project. it has had multiple redesigns over [some timespan] starting with FLC16 which I made before I even knew how CPUs actually worked. after that came the "batbox" and then "woof" which had two different versions. i now will not touch it anymore because its FINISHED.
=> woof, fantasy computer
woof is a very basic computer, only supporting a 256x128 black and white display, 8 button controller, 64kB memory, and this cpu I made called "mew" which is heavily inspired by the UXN. I'd been toying with the idea of being able to push/pop from BOTH ends of the stack, kind of like a queue, but also not. A quack, rather. Each opcode has a flag indicating whether it operates in the back or the front of the stack, along with a 'short mode' flag and a 'conditional' flag. Short mode means 16bit operations instead of 8bit. Conditional means that the top of the stack is popped, and the instruction is only executed if the byte is nonzero.
What are the advantages to using it over the UXN? Not many honestly. This was more of an experiment project just to see if I could do it, and because I thought it would be cool. I learned a lot over the course of the project and I'm glad that I made this, but I probably wont actually use it to make anything. Maybe a pong game to show it's capabilities sometime?
Programs are assembled from MEW assembly to machine code using a program called grao. Below is a Hello, World! program with explanations. You can see the obvious similarities between UXNtal and mew assembly.
=> the grao in question
@Console ( define console IO port at 0x00 )
!START ( define program start at 0x100 )
;text ( push the address of the string to the stack as a 16bit number )
@loop ( declare the start of the loop )
DUP2 RAM ( duplicate the address, then get one letter from the string )
DUP .Console WIO ( duplicate the letter and write it to the console )
vSWP vINC2 vSWP ( increment the address at the front of the stack )
;loop ROT ROT ( push the start of the loop to the queue, and then move it behind the letter )
JMP2? ( only jump back to the start of the loop if the value of the letter is nonzero )
BRK ( empty the queue and stop execution )
@text "Hello '20 "World! '0a '00
( 20 and 0a are ascii codes for space and linebreak respectively. 00 is the string's null terminator )
The program above is 34 bytes once assembled. 0.16ms execution time on my machine. feelsgoodman
I will also provide a little documentation in case someone was interested for some reason.
'number' refers to a byte or a short
READ: gets one byte from stdin
WRITE: puts one byte into stdout
WRITE: puts one byte into stderr
WRITE: kills the process
WRITE: defines the address in memory to be called for drawing each frame to screen. setting this will stop the process from exiting once it's finished.
WRITE: sets a pixel on the screen. high 8 bits are the X coordinate, low 7 bits are the Y coordinate. remaining 0x80 bit either sets the pixel on or off
WRITE: sets the address in memory of an 8x8 1bpp sprite (total of 8 bytes)
WRITE: identical to set pixel, but draws the indicated sprite instead
READ: gets one byte that indicates which buttons are currently being pressed. see below
WRITE: sets address in memory to be called when a button is pressed or released
each bit in the controller byte corresponds to one button