One of the nicest things about the PERQ is that it's soft-microcodable; users can write speed-critical pieces of code in the machine's native language, augment the existing instruction set, or even replace it altogether. This is a little bit of microcode I wrote in response to a challenge on ucam.chat (or was it oxbridge.tat? I don't remember now...) to come up with the shortest assembly language routine for ROT13 encoding a text string. Of course, on a PERQ this is a single instruction:
HEX OPCODE COMMENTS 13 ROT13 ; ROT13 encode the quadword aligned ; ASCIIZ string pointed to by the accumulatorImplementing the rest of the instruction set is left as an exercise for the reader :-)
A word of warning; I haven't tested this code... The original certainly has bugs in it: ARD12 made some changes and corrections.
Those who don't know any PERQ microcode might want to read ARD12's Introduction to Microcoding, parts one, two and three.
! rot13.mic -- microcode to implement a ROT13 instruction ! Probably doesn't work -- I wasn't able to test it! Define(Acc,100); ! set up some symbolic names for registers Define(D1,101); ! Acc is the actual bytecode accumulator Define(D2,102); ! On entry, Acc == ptr to quadword-aligned Define(D3,103); ! 0-terminated string for ROT13 encoding. Define(D4,104); Define(D5,105); Define(D6,106); Define(D7,107); Loc(114); ! right placement for Rot13 instruction ! Enter here from NextInst (hardware jump-on-next-bytecode) D2:=0; ! flag for end of string ! Loop back to here each quadword Lp: MA:=Acc, Fetch4R; ! get quadword from memory in reverse order... ! 2 tstates here if we need them TOS:=MDI, Push; ! here comes the data from that Fetch4 TOS:=MDI, Push; ! We now have 64bits of the string on the stack TOS:=MDI, Push; ! Note that putting data on the stack and TOS:=MDI, Push; ! adjusting the stack pointer are separate ops :-> D1:=TOS, Call(r13it); ! note the way we do the Pop after the Call D3:=D1, Pop; ! to save a t-state each instruction D1:=TOS, Call(r13it); D4:=D1, Pop; D1:=TOS, Call(r13it); D5:=D1, Pop; D1:=TOS, Call(r13it); D6:=D1, Pop; MA:=Acc, Store4; ! now we do the Store of this quadword -- t3 D3; ! t0 D4; ! t1 D5; ! t2 D6; ! t3 D2; if Neq Goto(Lp); ! if we didn't hit the terminator, get next quadword NextInst; ! get next bytecode ! call r13it with a word in D1. Returns with D1=Rot13'd equivalent. ! If D2=1, no action. Sets D2 if it hits a zero byte. r13it: D2; if Neq Return; ! do nothing if D2==1 TOS:=D1, Push; ! save for second byte RightShift(8); D1:=Shift, Call(r13b); ! get high byte, rot13 it D2; if Neq Return; ! was that end of string? D7:=TOS and 377, Pop; ! now low byte LeftShift(8); D1; TOS:=Shift, Push; ! push ROT13d high byte D1:=D7, Call(r13b); D1:=D1 or TOS, Pop; ! put both bytes back together Return; ! r13b: Rot13 a byte in D1. If byte == 0, set D2=1 r13b: D1; if Eql Goto(l1); ! if D1=0, set flag and quit D2:=1,Return; l1: D1-'A'; if Lss Return; ! byte below A -- ignore D1-'M'; if Gtr Goto(l2); l4: D1:=D1+15,Return; ! octal constant! l2: D1-'Z'; if Gtr Goto(l3); l5: D1:=D1-15, Return; l3: D1-'a'; if Lss Return; ! non-alpha chars in the middle D1-'m'; if Leq Goto(l4); D1-'z'; if Leq Goto(l5); Return;
(I've edited the original posting slightly...)
From: ard12@eng.cam.ac.uk (A.R. Duell) Newsgroups: alt.sys.perq Subject: Re: Silly microcode... Date: 6 May 1997 17:02:15 GMT Organization: University of Cambridge, England Message-ID: <5kno6n$rvb@lyra.csx.cam.ac.uk> References: <5knk36$2im@mnementh.trin.cam.ac.uk> pm215@cam.ac.uk (Peter Maydell) writes: >Since this is my first microcode program, I thought it deserved >a wider audience. I'd also like to know if I made any errors... I have a few comments.... >---------begin file rot13.mic---------- >! rot13.mic -- microcode to implement a ROT13 instruction >! Probably doesn't work -- I wasn't able to test it! Yes, you can. Put it somewhere sane, and use the JCS instruction to run it. There are _very_ few changes needed to do that. >Define(Acc,100); ! set up some symbolic names for registers >Define(D1,101); ! Acc is the actual bytecode accumulator >Define(D2,102); ! On entry, Acc == ptr to quadword-aligned >Define(D3,103); ! 0-terminated string for ROT13 encoding. >Define(D4,104); >Define(D5,105); >Define(D6,106); >Define(D7,107); I'd have used more meaningful names... >Loc(114); ! right placement for Rot13 instruction >! Enter here from NextInst (hardware jump-on-next-bytecode) >! Loop back to here each quadword >Lp: MA:=Acc, Fetch4R; ! get quadword from memory in reverse order... >!set flag for end of string. >D2:=0 Is one 'dummy' instruction correct here? I don't have the PERQ memory info in front of me, alas... >TOS:=MDI, Push; ! here comes the data from that Fetch4 >TOS:=MDI, Push; ! We now have 64bits of the string on the stack >TOS:=MDI, Push; ! Note that putting data on the stack and >TOS:=MDI, Push; ! adjusting the stack pointer are separate ops :-> >D1:=TOS, Call(r13it); ! note the way we do the Pop after the Call >D3:=D1, Pop; ! to save a t-state each time >D1:=TOS, Call(r13it); >D4:=D1, Pop; >D1:=TOS, Call(r13it); >D5:=D1, Pop; >D1:=TOS, Call(r13it); >D6:=D1, Pop; You can save some instructions here. Firstly, move the D1:=TOS phrase into the r13it routines. Then you can write : Call(r13it); D3:=D1,Call(r13it); ! The assignment will happen before the call... D4:=D1,Call(r13it); D5:=D1,Call(r13it); ! and the last word is now in D1 >MA:=Acc, Store4; ! now we do the Store of this quadword -- t3 >D3; ! t0 >D4; ! t1 >D5; ! t2 >D6; ! t3 Replace that last line with : D1; ! t3 Remember that the last routine left the ! value in D1 > >D2; >if Neq Goto(Lp); ! if we didn't hit the terminator, get next quadword >NextInst; ! get next bytecode You mean NextInst(0) here, I think. > >! call r13it with a word in D1. Returns with D1=Rot13'd equivalent. >! If D2=1, no action. Sets D2 if it hits a zero byte. >r13it: D2; >if Neq Return; ! do nothing if D2==1 >TOS:=D1, Push; ! save for second byte It would be better as (remember, we've removed the pops earlier): D1:=TOS; ! Read next byte off the stack. Leave the SP ! unchangerd. Remember that the D1:=TOS's have been removed >RightShift(8); No! You can't do this. This instruction _will_ change 'R', so the shift in the next instruction will read the wrong value. A better way would be to set up the shifter to do a Rotate(8) at the start of the program, and then to use that to swap the bytes in the word. >D1:=Shift, Call(r13b); ! get high byte, rot13 it >D2; >if Neq Return; ! was that end of string? >D7:=TOS and 377, Pop; ! now low byte >LeftShift(8); >D1; >TOS:=Shift, Push; ! push ROT13d high byte >D1:=D7, Call(r13b); >D1:=D1 or TOS, Pop; ! put both bytes back together >Return; Something like : r13it:D2, Rotate(8); ! Set up shifter to do a byteswap if Neq Return; ! If D2 <>0 then do nothing TOS; ! Copy next word to R lines D1:=shift and 377, call(r13b); ! Read high byte into D1, rot13 it D7:=D1 D1:=TOS and 377,pop,call(r13b); !rot13 it low byte D7; D1:=shift or D1; ! put the word back together return; >! r13b: Rot13 a byte in D1. If byte == 0, set D2=1 >r13b: D1; We now need to change this to : D2; D1, if Neq return; >if Eql Goto(l1); ! if D1=0, set flag and quit Surely that should be Neq, shouldn't it? You want to go (i.e. carry on) if D1 is non-zero. >D2:=1,Return; >l1: D1-'A'; >if Lss Return; ! byte below A -- ignore >D1-'M'; >if Gtr Goto(l2); >l4: D1:=D1+15,Return; ! octal constant! >l2: D1-'Z'; >if Gtr Goto(l3); >l5: D1:=D1-15, Return; >l3: D1-'a'; >if Lss Return; ! non-alpha chars in the middle >D1-'m'; >if Leq Goto(l4); >D1-'z'; >if Leq Goto(l5); >Return; I'm sure there's a neater way to do that, but I'll have to think about it... >--------end file------- -- -tony ard12@eng.cam.ac.uk The gates in my computer are AND,OR and NOT, not Bill