Virtual processor Simpleton 4
Virtual processor Simpleton 4
Github page: https://github.com/aa-dav/SimpX#readme (including most actual documentation)
Online emulator+assembler: https://aa-dav.github.io/(select one of the 'test*.asm' files in list and click menu 'Compile and run')
Some of 8bit-guy videos including covering of project 'Gigatron TTL' inspired me to make some ISAs and virtual PC around it.
This is fourth generation of the same idea (see below).
First of all: this ISA is definitely suboptimal.
The only goal is to make instruction format as simple as possible keeping programming flexible and non-esoteric.
I know that code density could be improved, leading to something like MSP 430 as a result. So, it's not an option.
Online emulator+assembler: https://aa-dav.github.io/(select one of the 'test*.asm' files in list and click menu 'Compile and run')
Some of 8bit-guy videos including covering of project 'Gigatron TTL' inspired me to make some ISAs and virtual PC around it.
This is fourth generation of the same idea (see below).
First of all: this ISA is definitely suboptimal.
The only goal is to make instruction format as simple as possible keeping programming flexible and non-esoteric.
I know that code density could be improved, leading to something like MSP 430 as a result. So, it's not an option.
Last edited by aa-dav on Sun Jun 11, 2023 9:33 pm, edited 7 times in total.
Re: Virtual processor Simpleton 4
Having previous version of Simpleton 3 it was easy to make quick progress in implementing Simpleton 4.
Most assembler commands have syntax:
where R is destination and Y and X are operands.
If X is omitted it becomes immediate 0.
Very simple assembler implementation works. I defined input-output port which behaves like console input/output in host environment (Linux/Windows) like now.
So this program:
So, it outputs 'Hello, world!" string twice as planned.
Most assembler commands have syntax:
Code: Select all
CMD R Y X
If X is omitted it becomes immediate 0.
Very simple assembler implementation works. I defined input-output port which behaves like console input/output in host environment (Linux/Windows) like now.
So this program:
Code: Select all
PORT_CONSOLE = $FFFF
move sp $70 ; Setup stack
move r0 str0 ; shortcut for addis r0 str0 0
call str_print ; shortcut for addis [ sp ] pc 2 : addis pc str_print 0
move r0 str0
call str_print
dw 0 ; STOP
str_print movet r1 [ r0 ]
jz .exit ; shortcut for cadd pc pc _calculated_constant_ (mix of offset and condition code)
move [ PORT_CONSOLE ] r1 ; output char to console
addi r0 r0 1 ; increment r0
move pc str_print ; jump to the beginning of procedure
.exit ret ; shortcut for move pc [ sp ]
str0 dw "Hello, world!" 10 0
Re: Virtual processor Simpleton 4
I am not experienced in schematics and hardware creation, but I hope some day I will have enough time to learn more about it and create Simpleton-based system in real hardware (FPGA).
Another thing which seems like challenge is interrupt handling.
The problem is: interrupt ignition (like in typical CPU) is a too complex thing for Simpleton base design: R = Y op X.
Simpleton cannot do CALL in single instruction, but interrupt is more complex task!
But I think it could be solved without breaking Simpleton rules.
Main instrument of solution is indirect PC reading: [ PC++ ] is used in so many places:
- reading instruction opcode
- reading immediate data (like $100)
- reading immediate indirect (like [ $100 ] (which is [ PSW ] in opcode, but it is implemented as [ PC++ ] giving address of memory cell we work with)
So... Let it be in this way: external signal of interrupt request engages special mode of [ PC++ ] indirect PC reading: it stops to read from main memory and post-increment PC. This is just bit in flags (PSW register) and if it is set - [ PC++ ] addressing mode starts to read from little internal CPU buffer of 8 or 16 words. This buffer contains program with immediate data (but it's invisible for any other reads).
It can contans program like this:
(7 words long)
That is - this internal 'special mode handler' executes CALL instruction (delayed until we exit special mode) which pushed return address to stack, points PC to interrupt handler, saves flags to stack and then disables 'special mode'. After that program continues in normal mode, but in irq handler.
So, return from interrupt will be usual code in ROM/RAM:
I think we could do many many things with such internal 'microcode' despite Simpleton has very simple instruction format.
Note, that jumps are not possible in this internal buffer because no special program counter is visible to CPU. But it exists and is set to 0 then special mode flag is set.
So, I think interrupts activation could be implemented not so hard in Simpleton 4 without complex logic of unusual instruction code/format.
Another thing which seems like challenge is interrupt handling.
The problem is: interrupt ignition (like in typical CPU) is a too complex thing for Simpleton base design: R = Y op X.
Simpleton cannot do CALL in single instruction, but interrupt is more complex task!
But I think it could be solved without breaking Simpleton rules.
Main instrument of solution is indirect PC reading: [ PC++ ] is used in so many places:
- reading instruction opcode
- reading immediate data (like $100)
- reading immediate indirect (like [ $100 ] (which is [ PSW ] in opcode, but it is implemented as [ PC++ ] giving address of memory cell we work with)
So... Let it be in this way: external signal of interrupt request engages special mode of [ PC++ ] indirect PC reading: it stops to read from main memory and post-increment PC. This is just bit in flags (PSW register) and if it is set - [ PC++ ] addressing mode starts to read from little internal CPU buffer of 8 or 16 words. This buffer contains program with immediate data (but it's invisible for any other reads).
It can contans program like this:
Code: Select all
addis [ sp ] pc 2 ; prepare return address, remember that direct PC reads/writes work as usual
move pc irq_handler ; point pc to irq_handler
or [ sp ] psw disable_special_mode_mask ; save flags without 'special mode' flag to stack (see below)
or psw psw disable_special_mode_mask ; This is there special [ PC++ ] mode will be shut off and program will continue from new PC
That is - this internal 'special mode handler' executes CALL instruction (delayed until we exit special mode) which pushed return address to stack, points PC to interrupt handler, saves flags to stack and then disables 'special mode'. After that program continues in normal mode, but in irq handler.
So, return from interrupt will be usual code in ROM/RAM:
Code: Select all
move flags [ sp ] ; restore flags
move pc [ sp ] ; return to program interrupted (RET pseudoop is available)
Note, that jumps are not possible in this internal buffer because no special program counter is visible to CPU. But it exists and is set to 0 then special mode flag is set.
So, I think interrupts activation could be implemented not so hard in Simpleton 4 without complex logic of unusual instruction code/format.
Re: Virtual processor Simpleton 4
One thing I always dislike in assemblers is symbolic syntax which is verbose and unintuitive.
So, I implemented math-notation for Simpleton 3 which uses C-like expressions like in 'R0 += [ label ]'.
Simpleton4 instruction set makes it harder to implement math-notation, so I started from classic assembler as above.
But math-notation will be implemented in Simpleton 4 too.
Most of instructions could be expressed as 'R = Y op X' where 'op' is operation sign.
If X is omitted it becomes literal '0' (zero).
If 'op' is omitted it becomes '+' sign, so pseudoinstruction 'move' is not needed anymore (see below).
Next instructions fulfill this pattern (example for R=R0, Y=R1 and X=[ label ]):
But there are 3 opcodes (right now) which fall out of this pattern and have special syntax:
First of all - it's 'inplace immediate' commands: addi, addis and rcci. These of them who updates flags use '<=' as sign of this special case.
The only exceptions is 'addis' which uses '<-' to signal that it's not updates flags.
Note, that 'addis a b -1' (negative inplace immediate) could be expressed in new syntax as 'a <- b + -1'. But also 'a <- b - 1' is correct (that is like 'sub' opcode). This is another reason for exclusion of addi(s) from regular '=' syntax.
Also, 'move' is as simple as: 'a <- b' which is shortcut for 'a <- b + 0'.
Note, that all pseudoops used for simplicity are still in place: 'jnz/call/ret' and so on.
So, example above could be rewritten as:
So, I implemented math-notation for Simpleton 3 which uses C-like expressions like in 'R0 += [ label ]'.
Simpleton4 instruction set makes it harder to implement math-notation, so I started from classic assembler as above.
But math-notation will be implemented in Simpleton 4 too.
Most of instructions could be expressed as 'R = Y op X' where 'op' is operation sign.
If X is omitted it becomes literal '0' (zero).
If 'op' is omitted it becomes '+' sign, so pseudoinstruction 'move' is not needed anymore (see below).
Next instructions fulfill this pattern (example for R=R0, Y=R1 and X=[ label ]):
Code: Select all
02 - ADDS - r0 = r1 +s [ label ] ; add silent (doesn't update flags)
03 - ADD - r0 = r1 + [ label ] ; add
04 - ADC - r0 = r1 +c [ lavel ] ; add with carry
05 - SUB - r0 = r1 - [ label ] ; sub
06 - SBC - r0 = r1 -c [ label ] ; sub with carry
07 - AND - r0 = r1 & [ label ] ; and
08 - OR - r0 = r1 | [ label ] ; or
09 - XOR - r0 = r1 ^ [ label ] ; xor
0A - CMP - r0 = r1 ? [ label ] ; compare (as Y - X), op updates flags and returns Y
0B - CADD - r0 = r1 +? [ label ] ; conditional add. never updates flags.
0D - RRC - r0 = r1 >> [ label ] ; rotate Y right (cyclic) by X bits
Code: Select all
00 - ADDIS - r0 <- r1 - 1 ; add Y with INPLACE immediate in XI+X SILENT (flags are not updated)
01 - ADDI - r0 <= r1 + 3 ; add Y with INPLACE immediate in XI+X
0C - RRCI - r0 <= r1 >> 15 ; rotate Y right (cyclic) by INPLACE immediate bits
The only exceptions is 'addis' which uses '<-' to signal that it's not updates flags.
Note, that 'addis a b -1' (negative inplace immediate) could be expressed in new syntax as 'a <- b + -1'. But also 'a <- b - 1' is correct (that is like 'sub' opcode). This is another reason for exclusion of addi(s) from regular '=' syntax.
Also, 'move' is as simple as: 'a <- b' which is shortcut for 'a <- b + 0'.
Note, that all pseudoops used for simplicity are still in place: 'jnz/call/ret' and so on.
So, example above could be rewritten as:
Code: Select all
PORT_CONSOLE = $FFFF
sp <- $70 ; Setup stack
r0 <- str0 ; shortcut for addis r0 str0 0
call str_print
r0 <- str0
call str_print
dw 0 ; STOP
str_print r1 <= [ r0 ] ; testing move (addi r1 [ r0 ] 0)
jz .exit
[ PORT_CONSOLE ] <- r1 ; output char to console
r0 <- r0 + 1 ; increment r0
pc <- str_print ; jump to the beginning of procedure
.exit ret ; shortcut for move pc [ sp ]
str0 dw "Hello, world!" 10 0
Last edited by aa-dav on Thu Mar 25, 2021 10:28 pm, edited 4 times in total.
Re: Virtual processor Simpleton 4
Before long you'll be looking through the APL unicode points and thinking you want to use those for your assembly mnemonics
Re: Virtual processor Simpleton 4
New (math-)assembler syntax is implemented and pushed to github.
Keyword 'mode' switches between modes: 'mode classic' and 'mode new'.
So, code just above should be started with 'mode new' line.
Keyword 'mode' switches between modes: 'mode classic' and 'mode new'.
So, code just above should be started with 'mode new' line.
Re: Virtual processor Simpleton 4
During discussion on another forum I was gifted with excellent idea!
(Many thanks to Lethargeek from https://hype.retroscene.org (russian resource)!)
In Simpleton 4 writing to indirect PC (like in 'move [ pc ] $100') is forbidden because of total meaninglessness (unlike indirect PC reading which implements 16-bit immediates).
But also 3-operand-style 'cmp' instruction acts strange ( like in 'r0 = r0 ? r1' - comparison returns first argument (Y) from ALU as result to be in touch with Simpleton architecture).
The idea is to ignore writing of result from ALU if destination (R) is [ pc ]!
ALU updates flags in operation, but result is ignored if destionation is [ pc ].
I bind keyword 'void' to indirect writing to PC in assembler and this allows to make non-destructing comparisons:
or bit tests (of any kind):
...or comparison of number with constant in range -8..+7 via one-word instruction (inplace immediate):
...or checking of i-th bit of operand via placing it in carry flag during RRCI instruction execution:
This is truly amazing: by exploiting forbidden destination in this way we free up 'CMP' opcode and get a lot of more instructions and techniques in return!
Github page (code and documentation, link is in the very beginning of theme) already has these changes and it works!
(Many thanks to Lethargeek from https://hype.retroscene.org (russian resource)!)
In Simpleton 4 writing to indirect PC (like in 'move [ pc ] $100') is forbidden because of total meaninglessness (unlike indirect PC reading which implements 16-bit immediates).
But also 3-operand-style 'cmp' instruction acts strange ( like in 'r0 = r0 ? r1' - comparison returns first argument (Y) from ALU as result to be in touch with Simpleton architecture).
The idea is to ignore writing of result from ALU if destination (R) is [ pc ]!
ALU updates flags in operation, but result is ignored if destionation is [ pc ].
I bind keyword 'void' to indirect writing to PC in assembler and this allows to make non-destructing comparisons:
Code: Select all
sub void A B ; acts like 'cmp A B' in many other ISAs
jnz ...
Code: Select all
and void r0 $0001
jz ...
Code: Select all
addi void r0 -3
jz ... ; r0 is equal to 3
Code: Select all
rrci void r0 3 ; CF gets bit 3
jc ... ; jump if CF=1
Github page (code and documentation, link is in the very beginning of theme) already has these changes and it works!
Re: Virtual processor Simpleton 4
Emulation of SimpX (Simpleton-based virtual old-school-PC) goes online!
Link: https://aa-dav.github.io/
To run first demo select file 'test0.asm' in the left panel and click 'Emulation->Run' menu.
It' interesting to hear about FPS (are shown in the bottom status line) with full-screen and 200/300/400% (selected in 'View') menu. To go back to full-document scale just change size of Browser window in any way (for example: minimize/maximize).
It tries to be simple as it's CPU, so video mode tries to cover bitmap-based and tile-based modes at the same time trying to be convenient for both: tile-based graphics and bitmap-based. There is single video mode there 8x8 tiles are arranged in 256x256 bitmap from left-to-right top-down manner as it's 2d bitmap and not linear tile descriptor array. There are 1024 (32x32) tiles possible so if we fill tilemap (32x32 word) with increasing numbers 0..1023 we get linear screen bitmap for linear quick screen buffer to draw.
Each tile has 4bpp 16-colors, but tile number (in word of memory (Simpleton has no bytes at all)) also stores in upper 4 bits subpalette number, so there is 16 colors in tile and 256 colors on the screen. Effective screen is 256x192 pixels wide with ability to hide rightmost 8 pixel columns to implement seamless hardware scrolling.
Link: https://aa-dav.github.io/
To run first demo select file 'test0.asm' in the left panel and click 'Emulation->Run' menu.
It' interesting to hear about FPS (are shown in the bottom status line) with full-screen and 200/300/400% (selected in 'View') menu. To go back to full-document scale just change size of Browser window in any way (for example: minimize/maximize).
It tries to be simple as it's CPU, so video mode tries to cover bitmap-based and tile-based modes at the same time trying to be convenient for both: tile-based graphics and bitmap-based. There is single video mode there 8x8 tiles are arranged in 256x256 bitmap from left-to-right top-down manner as it's 2d bitmap and not linear tile descriptor array. There are 1024 (32x32) tiles possible so if we fill tilemap (32x32 word) with increasing numbers 0..1023 we get linear screen bitmap for linear quick screen buffer to draw.
Each tile has 4bpp 16-colors, but tile number (in word of memory (Simpleton has no bytes at all)) also stores in upper 4 bits subpalette number, so there is 16 colors in tile and 256 colors on the screen. Effective screen is 256x192 pixels wide with ability to hide rightmost 8 pixel columns to implement seamless hardware scrolling.
Re: Virtual processor Simpleton 4
Some guy with nickname Total Vacuum made wolf3d-like demo for SimpX.
It's available in link https://aa-dav.github.io/
Select 'test08-wold3d.asm' in list and press menu 'Emulator->Compile and run'.
It's better to select View->Set 400% to select proper screen ratio.
Control keys of this demo A/W/D (rotation and walk forward).
If you take a better look at the source code you probably will be amazed by it's cryptic nature.
It's really effect of Forth compilator which Total Vacuum has and adapted for my platform.
This is how real program looks like:
Graphics output is based on the 'putpixel' word (in terms of Forth) which treats screen of my architecture for simplicity as 4x4 squares ('pixels'). So, at base level of his compiler for SimpX there are two word defined - 'putpixel' and 'key':
That's all that is used for this demo.
Great job.
It's available in link https://aa-dav.github.io/
Select 'test08-wold3d.asm' in list and press menu 'Emulator->Compile and run'.
It's better to select View->Set 400% to select proper screen ratio.
Control keys of this demo A/W/D (rotation and walk forward).
If you take a better look at the source code you probably will be amazed by it's cryptic nature.
It's really effect of Forth compilator which Total Vacuum has and adapted for my platform.
This is how real program looks like:
Code: Select all
% UF\SIMPX\core.uf
% UF\SIMPX\stdio.uf
: rshift [ $ ` r0 <= [ sp ]` ` r1 = r0 & $8000` ` r0 = r0 & $FFFE` ` r0 <= r0 >> 1` ` [ sp ] = r0 | r1` $ ] ;
:: [ ] LocX ; :: [ ] LocY ;
:: [ ] Angle ;
:: [ ] color ;
:: [ ] x ; :: [ ] y ;
:: [ ] h ;
:: [ ] j1 ; :: [ ] j2 ;
:: [ ] i0 ; :: [ ] j0 ;
:: [ ] u0 ; :: [ ] v0 ;
:: [ ] da ; :: [ ] db ;
:: [ ] a ; :: [ ] b ;
:: [ ] u ; :: [ ] v ;
:: [ ] u1 ; :: [ ] v1 ;
:: [ ] a1 ; :: [ ] b1 ;
:: [ ] i ; :: [ ] j ;
:: [ ] di ; :: [ ] dj ;
:: [ ] wall ;
:: [ 13 ] Map ; :: [ 16 ] CosTable ;
:: WIDTH 64 ; :: HEIGHT 48 ;
:: BITS 5 ; :: STEP 32 \ 1<<BITS \ ; :: MASK 31 \ STEP-1 \ ;
:: over 1 ? ;
: abs # 15 rshift # + 1 + * ;
: coz abs 8 * 64 - abs 2 * 64 - ;
: cos Angle @ CosTable + @ ;
: sin Angle @ 4 - 15 & CosTable + @ ;
:: span
color ! x !
HEIGHT # # # h ! [ # y @ * b1 @ < { h ! 0 } ]
h @ - 1 rshift j1 !
j1 @ h @ + j2 !
j1 @ [ x @ over 0 putpixel ]
h @ [ x @ over j1 @ + color @ putpixel ]
j2 @ - [ x @ over j2 @ + 0x2222 putpixel ]
;
: scene
LocX @ # BITS rshift i0 ! MASK & u0 !
LocY @ # BITS rshift j0 ! MASK & v0 !
sin # 3 rshift da ! cos 1 rshift + ` [ sp ] <= [ sp ] >> 13` \ 8 * \ b !
cos # 3 rshift db ! sin 1 rshift - ` [ sp ] <= [ sp ] >> 13` \ 8 * \ a !
WIDTH [
a @ 0 < { 0xFFFF u0 @ 0 a @ - ~ 1 STEP u0 @ - a @ } a1 ! u ! di !
b @ 0 < { 0xFFFF v0 @ 0 b @ - ~ 1 STEP v0 @ - b @ } b1 ! v ! dj !
u @ # STEP - x ! b1 @ # ` [ sp ] <= [ sp ] >> 11` \ 32 * \ \ <<BITS \ u1 ! * u !
v @ # STEP - y ! a1 @ # ` [ sp ] <= [ sp ] >> 11` \ 32 * \ \ <<BITS \ v1 ! * v !
i0 @ i !
j0 @ j !
1 (
u @ v @ < # {
x @ STEP + x !
v @ u @ - v !
u1 @ u !
i @ di @ + i !
~
y @ STEP + y !
u @ v @ - u !
v1 @ v !
j @ dj @ + j !
}
wall !
i @ Map + @ j @ rshift 1 & )
# wall @ { a1 @ b1 ! x @ y ! } i @ j @ + 3 & { 0x1111 ~ 0x4444 } span
a @ da @ + a !
b @ db @ - b !
]
;
:: run
LocX @ cos 4 rshift + # x ! BITS rshift i !
LocY @ sin 4 rshift + # y ! BITS rshift j !
i @ Map + @ j @ rshift 1 & { x @ LocX ! y @ LocY ! scene }
;
: turn Angle @ + 15 & Angle ! scene ;
:: main
0b000000000000000
0b011111111111110
0b010101010101010
0b011111111111110
0b011110010011110
0b011110111011110
0b011111101111110
0b011110111011110
0b011110010011110
0b011111111111110
0b010101010101010
0b011111111111110
0b000000000000000
13 [ $ over Map + ! ]
16 [ # # coz $ CosTable + ! ]
4 Angle !
40 # LocX ! LocY !
scene
1 (
key # #
'w' = { run }
'a' = { 0xFFFF turn }
'd' = { 1 turn }
1 )
;
main
`@end`
Code: Select all
: putpixel \ x y c \
` r0 <= [ sp ]`
` r1 = [ sp ] >> 8` ` r1 = r1 + $8000` ` r1 = r1 + [ sp ]`
` [ r1 ] <- r0 `
` r1 = r1 + $40` ` [ r1 ] <- r0 `
` r1 = r1 + $40` ` [ r1 ] <- r0 `
` r1 = r1 + $40` ` [ r1 ] <- r0 `
;
:: key ` [ LastKey ] <- 0` ` call inputChar` ` [ sp ] <= r0` ;
Great job.
Re: Virtual processor Simpleton 4
Neat Had some fun with this, tonight.