* @(#)emulate.s 7.2 (Berkeley) 1/16/87
* String instruction emulation - MicroVAX only. These routines are called
* from locore.s when an "emulate" fault occurs on the MicroVAX. They are
* called with the stack set up as follows:
* (sp): Return address of trap handler
* 4(sp): Instruction Opcode (also holds PSL result from emulator)
* 36(sp): old Register 11
* 40(sp): old Register 10
* 52(sp): TOS before instruction
* R11 and r10 are available for use. If any routine needs to use r9-r1
* they need to save them first (unless those registers are SUPPOSED to be
* messed with by the "instruction"). These routines leave their results
* in registers 0-5 explicitly, as needed, and use the macros defined below
* to link up with calling routine.
#define savepsl movpsl 4(sp)
#define setpsl(reg) movl reg,4(sp)
#define overflowpsl movl $2,4(sp)
#define argub(num,reg) movzbl 8+4*num(sp),reg
#define arguw(num,reg) movzwl 8+4*num(sp),reg
#define argul(num,reg) movl 8+4*num(sp),reg
#define argb(num,reg) cvtbl 8+4*num(sp),reg
#define argw(num,reg) cvtwl 8+4*num(sp),reg
#define argl(num,reg) movl 8+4*num(sp),reg
#define toarg(reg,num) movl reg,8+4*num(sp)
argl(1,r11) # (1) table address == r11
argl(2,r0) # (2) initial crc == r0
argl(4,r3) # (4) source address == r3
arguw(3,r2) # (3) source length == r2
arguw(1,r0) # (1) source length == r0
argl(2,r1) # (2) source address == r1
argub(3,r11) # (3) fill character == r11
argl(4,r3) # (4) table address == r3
argl(6,r5) # (6) destination address == r5
arguw(5,r4) # (5) destination length == r4
arguw(1,r0) # (1) source length == r0
argl(2,r1) # (2) source address == r1
argub(3,r11) # (3) escape character == r11
argl(4,r3) # (4) table address == r3
argl(6,r5) # (6) destination address == r5
arguw(5,r4) # (5) destination length == r4
argl(2,r10) # (2) substring address == r10
arguw(3,r2) # (3) source length == r2
argl(4,r3) # (4) source address == r3
arguw(1,r11) # (1) substring length == r11
jeql Lmatchc_out # temp source address == r1
addl2 r10,r11 # temp substring address == r0
argl(2,r1) # (2) string address == r1
argub(4,r2) # (4) character-mask == r2
argl(3,r3) # (3) table address == r3
arguw(1,r0) # (1) string length == r0
argl(2,r1) # (2) string address == r1
argub(4,r2) # (4) character-mask == r2
argl(3,r3) # (3) table address == r3
arguw(1,r0) # (1) string length == r0
argub(1,r11) # (1) character == r11
argl(3,r1) # (3) string address == r1
arguw(2,r0) # (2) string length == r0
jeql Lskpc_out # forget zero length strings
tstl r0 # be sure of condition codes
argub(1,r11) # (1) character == r11
argl(3,r1) # (3) string address == r1
arguw(2,r0) # (2) string length == r0
jeql Lskpc_out # forget zero length strings
tstl r0 # be sure of condition codes
argl(2,r1) # (2) string1 address == r1
argl(3,r3) # (3) string2 address == r3
arguw(1,r0) # (1) strings' length == r0
argl(2,r1) # (2) string1 address == r1
argub(3,r11) # (1) fill character == r11
arguw(4,r2) # (1) string2 length == r2
argl(5,r3) # (3) string2 address == r3
arguw(1,r0) # (1) string1 length == r0
sobgtr r2,Lcmpc5_str2loop
sobgtr r0,Lcmpc5_str1loop
* Packed Decimal string operations
toarg(r9,6) # save register r9 in arg6 spot
arguw(1,r11) # (1) source length == r11
argl(2,r10) # (2) source address == r10
arguw(3,r9) # (3) destination length == r9
argl(4,r3) # (4) destination address == r3
# arg4 will be needed later
# arg5 holds destination address of LSNibble temporarily
addl2 r11,r10 # source address of LSNibble
incl r11 # source length is in bytes
addl2 r9,r3 # r3 = destination address of LSNibble
incl r9 # destination length is in bytes
toarg(r3,5) # stored in arg5 spot
extzv $0,$4,(r3),r2 # set standard +/- indicators in destination
extzv $0,$4,(r10),r2 # r2 = standard +/- of source
cmpl r11,r9 # if source is longer than destination
movl r9,r11 # set source length == destination length
extzv $4,$4,(r3),r9 # r9 = LSDigit of destination
extzv $4,$4,(r10),r1 # r1 = LSDigit of source
cmpl r0,r2 # if signs of operands are not equal
jeql Laddp4_same # do a subtraction
clrl r2 # r2 is non-zero if result is non-zero
subl2 r1,r9 # r9 = "addition" of operands' high nibble
jbr L119 # jump into addition loop
addl2 r0,r1 # r1 = carry + next (low) nibble of source
subl2 r0,r1 # r1 -= next (low) nibble of destination
jgeq L121 # if negative result
mnegl $1,r9 # r9 == carry = -1
addl2 $10,r1 # r1 == result += 10
clrl r9 # r9 == carry = 0
insv r1,$0,$4,(r3) # store result low nibble
addl2 r0,r9 # r9 = carry + next (high) nibble of source
subl2 r0,r9 # r9 -= next (high) nibble of destination
jgeq L117 # if negative result
mnegl $1,r1 # r1 == carry = -1
addl2 $10,r9 # r9 == result += 10
clrl r1 # r1 == carry = 0
insv r9,$4,$4,(r3) # store result high nibble
bisl2 r9,r2 # r2 is non-zero if result is non-zero
decl r11 # while (--source length)
argl(4,r10) # r10 = address of destination MSNibble
addl2 r0,r1 # r1 == carry += next (low) nibble
jgeq L127 # if less than zero
movl r1,r9 # r9 == carry (must be -1)
movl $9,r1 # r1 == result = 9
clrl r9 # r9 == carry = 0
insv r1,$0,$4,(r3) # store result
addl2 r0,r9 # r9 == carry += next (high) nibble
jgeq L129 # if less than zero
movl r9,r1 # r1 == carry (must be -1)
movl $9,r9 # r9 == result = 9
insv r9,$4,$4,(r3) # store result
tstl r1 # if carry out of MSN then fix up result
argl(5,r3) # r3 == address of LSN of destination
cmpl r0,NEGATIVE # switch sign of result
extzv $4,$4,(r3),r0 # normalize result (carry out of MSN into LSN)
subl3 r0,$10,r9 # r9 = 10 - destination LSNibble
cmpl r3,r10 # while (not at MSNibble)
extzv $0,$4,(r3),r0 # low nibble = (9 + carry) - low nibble
extzv $4,$4,(r3),r0 # high nibble = (9 + carry) - high nibble
Laddp4_same: # operands are of the same sign
addl2 r0,r1 # r1 == carry += next (low) nibble of dest
addl2 r0,r1 # r1 += next (low) nibble of source
cmpl r1,$9 # if result > 9
movl $1,r9 # r9 == carry = 1
subl2 $10,r1 # r1 == result -= 10
clrl r9 # r9 == carry = 0
insv r1,$0,$4,(r3) # store result
addl2 r0,r9 # ditto for high nibble
sobgtr r11,Laddp4_same_loop # while (--source length)
argl(4,r10) # r10 = destination address of MSNibble
extzv $0,$4,(r3),r0 # propagate carry up to MSNibble of destination
argl(5,r3) # r3 = destination address of LSNibble
savepsl # remember that for condition codes
insv POSITIVE,$0,$4,(r3) # make sure sign of result is positive
cmpl r0,NEGATIVE # if result is negative
mnegl r2,r2 # remember THAT in Cond Codes
argl(6,r9) # restore r9 from stack
arguw(1,r11) # (1) string length == r11
argl(2,r10) # (1) source address == r10
argl(3,r3) # (1) destination address == r3
# we will need arg2 and arg3 later
clrl r2 # r2 == non-zero if source is non-zero
ashl $-1,r11,r11 # length is number of bytes, not nibbles
bisb2 (r10),r2 # keep track of non-zero source
movb (r10)+,(r3)+ # move two nibbles
sobgtr r11,Lmovp_copy # loop for length of source
extzv $4,$4,(r10),r0 # look at least significant nibble
extzv $0,$4,(r10),r0 # check sign nibble
Lmovp_neg: # source was negative
tstl r2 # set condition codes
movb (r10),(r3) # move last byte if non-zero result
movb POSITIVE,(r3) # otherwise, make result zero and positive
* Definitions for Editpc instruction
* Here are the commands and their corresponding hex values:
* r4 is carved up as follows:
* -------------------------------------------
* -------------------------------------------
* fill character is stuffed into arg5 space
* sign character is stuffed into arg6 space
#define setsignif bisl2 $1,r4
#define clsignif bicl2 $1,r4
#define setoverflow bisl2 $2,r4
#define cloverflow bicl2 $2,r4
#define setzero bisl2 $4,r4
#define clzero bicl2 $4,r4
#define setnegative bisl2 $8,r4
#define clnegative bicl2 $8,r4
#define putfill movb arg5,(r5)+
#define setfill(reg) movb reg,arg5
#define putsign movb arg6,(r5)+
#define setsign(reg) movb reg,arg6
arguw(1,r11) # (1) source length == r11
argl(2,r10) # (2) source address == r10
argl(3,r3) # (3) pattern address == r3
argl(4,r5) # (4) destination address == r5
# we will need arg1 and arg2 later
# arg5 and arg6 are used for fill and sign - r0 is free
setfill($32) # fill character is ' '
setsign($32) # sign character is ' '
ashl $-1,r11,r11 # source length / 2
extzv $4,$4,(r2),r1 # r1 == least significant nibble of source
tstb -(r2) # loop over source packed decimal number
incl r1 # r1 is non-zero if source is non-zero
jeql L172 # source is zero - set flags
jeql L9998 # source is negative - set sign and flags
setsign($45) # sign character is '-'
arguw(1,r2) # (1) source length == r2
movzbl (r3)+,r11 # get next edit command (pattern)
extzv $0,$4,r11,r1 # command has a "count" arg - into r1
ashl $-4,r11,r11 # and shift over
jbc $6,r11,L181 # "shift" those commands > 64 to 16 and up
caseb r11,$0,$0x18 # "do" the command
# r11 is available for use, r1 has "count" in it
.word Le_end - Lcaseb_label # 00
.word Le_end_float - Lcaseb_label # 01
.word Le_clear_signif - Lcaseb_label # 02
.word Le_set_signif - Lcaseb_label # 03
.word Le_store_sign - Lcaseb_label # 04
.word Le_end - Lcaseb_label # 05
.word Le_end - Lcaseb_label # 06
.word Le_end - Lcaseb_label # 07
.word Le_fill - Lcaseb_label # 80
.word Le_move - Lcaseb_label # 90
.word Le_float - Lcaseb_label # a0
.word Le_end - Lcaseb_label # b0
.word Le_end - Lcaseb_label # c0
.word Le_end - Lcaseb_label # d0
.word Le_end - Lcaseb_label # e0
.word Le_end - Lcaseb_label # f0
.word Le_load_fill - Lcaseb_label # 40
.word Le_load_sign - Lcaseb_label # 41
.word Le_load_plus - Lcaseb_label # 42
.word Le_load_minus - Lcaseb_label # 43
.word Le_insert - Lcaseb_label # 44
.word Le_blank_zero - Lcaseb_label # 45
.word Le_replace_sign - Lcaseb_label # 46
.word Le_adjust_input - Lcaseb_label # 47
jbs SIGNIFBIT,r4,Ledit_case # if significance not set
putsign # drop in the sign
jbs NEGATIVEBIT,r4,Lpattern_inc # if non-negative
jbs NEGATIVEBIT,r4,Le_load_sign # if negative load the sign
incl r3 # else increment pattern
jbc SIGNIFBIT,r4,L196 # if significance set, put next byte
L196: # else put in fill character
# and throw away character in pattern
Le_replace_sign: # we don't do anything with
Lpattern_inc: # replace sign `cause we don't
incl r3 # get negative zero
jbc ZEROBIT,r4,Lpattern_inc # if zero
movzbl (r3)+,r11 # next byte is a count
subl2 r11,r5 # to back up over output and replace
putfill # with fill character
movzbl (r3)+,r0 # get count of nibbles from pattern
jgeq Ledit_case # if length of source is > this number
L204: # discard digits in source
jlbc r2,L206 # use low bit of length to choose nibble
bitb $0xf0,(r10) # high nibble
setsignif # set significance and overflow if
setoverflow # wasted digit is non-zero
bitb $0xf,(r10) # low nibble
incl r10 # increment to next byte
decl r2 # decrement source length
incl r11 # continue `till we're out of excess
tstl r1 # put (count in r1) fill characters
tstl r1 # move (count in r1) characters
jeql Ledit_case # from source to destination
jlbc r2,L215 # read a nibble
decl r2 # source length CAN go negative here...
setsignif # set significance
jbc SIGNIFBIT,r4,L219 # if significance set
addb3 $48,r11,(r5)+ # put '0' + digit into destination
L219: # else put fill character
Le_float: # move with floating sign character
decl r2 # source length CAN go negative here...
argb(1,r11) # (1) scale (number to shift) == r11
arguw(2,r10) # (2) source length == r10
argl(3,r1) # (3) source address == r1
argub(4,r2) # (4) rounding factor == r2
arguw(5,r3) # (5) destination length == r3
toarg(r6,3) # arg3 holds register 6 from caller
argl(6,r6) # (6) destination address == r6
# arg1 is used for temporary storage
# arg2 holds "even or odd" destination length
# arg4 is used as general storage
# arg5 is used as general storage
ashl $-1,r3,r0 # destination length is number of bytes
addl2 r0,r6 # destination address == least sig nibble
toarg(r6,1) # save in arg1 spot for later
addl2 r0,r1 # source address == least sig nibble
extzv $0,$4,(r1),r0 # determine sign of source
clrl arg2 # arg2 is 1 if dstlen is even, 0 if odd
bisl2 $1,r3 # r3<0> counts digits going into destination
L246: # and is flip-flop for which nibble to
tstl r11 # write in destination (1 = high, 0 = low)
jgeq Lashp_left # (it must start out odd)
addl2 r11,r10 # scale is negative (right shift)
clrl r10 # test for shifting whole number out
addl2 r0,r1 # source address == MSNibble to be shifted off
addl2 r0,r2 # round = last nibble to be shifted off + round
addl2 r0,r2 # round = last nibble to be shifted off + round
Lashp_setround: # r11<0> now is flip-flop for which nibble to
incl r11 # read from source (1 == high, 0 == low)
cmpl r2,$9 # set rounding factor to one if nibble shifted
jleq Lashp_noround # off + round argument was 10 or greater
jlbs r3,L257 # don't need to clear high nibble twice
clrb -(r6) # clear low (and high) nib of next byte in dest
sobgtr r3,L258 # move to next nibble in destination, but
incl r3 # don't go beyond the end.
Lashp_left: # while scale is positive
incl r11 # r11<0> is flip-plop ... (incl sets it to one)
clrl r2 # no more rounding
clrl arg4 # arg4 will be used for result condition codes
incl r11 # flip the source nibble flip/flop
addl2 r0,r2 # round += next nibble
cmpl r2,$10 # if round == 10
clrl arg5 # then result = 0 and round = 1
movl r2,arg5 # store result and round = 0
bisl2 arg5,arg4 # remember if result was nonzero in arg4
decl r3 # move to next nibble early to check
cmpl r3,arg2 # if we've moved passed destination limits
jgeq Lashp_noovfl # test the result for possible overflow
movl arg2,r3 # ignore zero nibbles
tstl arg5 # if the nibble was non-zero, overflow
insv arg5,$4,$4,(r6) # put the result into destination (high or low)
sobgtr r10,Lashp_shloop # loop for length of source
tstl r2 # take care of round out of high nibble
cmpl r3,arg2 # if we've moved passed destination limits
jlss Lashp_overfl # then overflow
insv arg5,$4,$4,(r6) # put the round into destination (high or low)
argl(1,r10) # r10 = address of destination LSNibble
argl(6,r3) # r3 = address of destination MSNibble
movl arg4,r11 # r11 = non-zero if destination == non-zero
clrb -(r6) # fill up MSNs of destination with zeros
extzv $0,$4,(r10),r0 # test for negative result
jneq Lashp_out # turn -0 into 0
insv POSITIVE,$0,$4,(r10)
argl(3,r6) # restore r6 from stack
Lashp_overfl: # do overflow
arguw(2,r10) # (2) destination length == r10
argl(3,r3) # (3) destination address == r3
addl2 r10,r3 # destination address points to Least Sig byte
incl r10 # length is # of bytes, not nibbles
argl(1,r11) # (1) source == r11
movb NEGATIVE,(r3) # source is negative
subl3 r11,r1,r2 # r2 = source mod 10
mnegl r0,r11 # source = -(source / 10)
movb POSITIVE,(r3) # source is non-negative
subl3 r1,r11,r2 # r2 = source mod 10
movl r0,r11 # source = source / 10
insv r2,$4,$4,(r3) # store least significant digit
Lcvtlp_loop: # while source is non-zero
decl r10 # and for length of destination ...
divl3 $10,r11,r1 # r1 = source / 10
subl2 r0,r11 # source = source mod 10
movb r11,-(r3) # store low "nibble" in next significant byte
divl3 $10,r1,r11 # source = r1 / 10
subl2 r0,r1 # r1 = source mod 10
insv r1,$4,$4,(r3) # store high nibble
jneq Lcvtlp_loop # quit if source becomes zero
Lcvtlp_zloop: # fill any remaining bytes with zeros
clrl r1 # r0 is already zero
arguw(1,r11) # (1) source length == r11
argl(2,r10) # (2) source address == r10
clrl r3 # r3 == destination
movl r10,r1 # r1 set up now for return
ashl $-1,r11,r11 # source length is number of bytes
Lcvtpl_loop: # for source length
mull2 $10,r3 # destination *= 10
addl2 r0,r3 # destination += high nibble
mull2 $10,r3 # destination *= 10
addl2 r0,r3 # destination += low nibble
Lcvtpl_zero: # least significant byte
addl2 r0,r3 # dest = 10 * dest + high nibble
extzv $0,$4,(r10),r2 # test sign nibble
Lcvtpl_neg: # source was negative - negate destination
* Emulation OpCode jump table:
* ONLY GOES FROM 0xf8 (-8) TO 0x3B (59)
#define NOEMULATE .long noemulate
#define EMULATE(a) .long _EM/**/a
/* f8 */ EMULATE(ashp); EMULATE(cvtlp); NOEMULATE; NOEMULATE
/* fc */ NOEMULATE; NOEMULATE; NOEMULATE; NOEMULATE
/* 00 */ NOEMULATE; NOEMULATE; NOEMULATE; NOEMULATE
/* 04 */ NOEMULATE; NOEMULATE; NOEMULATE; NOEMULATE
/* 08 */ EMULATE(cvtps); EMULATE(cvtsp); NOEMULATE; EMULATE(crc)
/* 0c */ NOEMULATE; NOEMULATE; NOEMULATE; NOEMULATE
/* 10 */ NOEMULATE; NOEMULATE; NOEMULATE; NOEMULATE
/* 14 */ NOEMULATE; NOEMULATE; NOEMULATE; NOEMULATE
/* 18 */ NOEMULATE; NOEMULATE; NOEMULATE; NOEMULATE
/* 1c */ NOEMULATE; NOEMULATE; NOEMULATE; NOEMULATE
/* 20 */ EMULATE(addp4); EMULATE(addp6); EMULATE(subp4); EMULATE(subp6)
/* 24 */ EMULATE(cvtpt); EMULATE(mulp); EMULATE(cvttp); EMULATE(divp)
/* 28 */ NOEMULATE; EMULATE(cmpc3); EMULATE(scanc); EMULATE(spanc)
/* 2c */ NOEMULATE; EMULATE(cmpc5); EMULATE(movtc); EMULATE(movtuc)
/* 30 */ NOEMULATE; NOEMULATE; NOEMULATE; NOEMULATE
/* 34 */ EMULATE(movp); EMULATE(cmpp3); EMULATE(cvtpl); EMULATE(cmpp4)
/* 38 */ EMULATE(editpc); EMULATE(matchc); EMULATE(locc); EMULATE(skpc)
* The following is called with the stack set up as follows:
* 32(sp): Operand 7 (unused)
* 36(sp): Operand 8 (unused)
* 48(sp): TOS before instruction
* Each individual routine is called with the stack set up as follows:
* (sp): Return address of trap handler
* 4(sp): Opcode (will get return PSL)
* 36(sp): saved register 11
* 40(sp): saved register 10
* 52(sp): TOS before instruction
movl r11,32(sp) # save register r11 in unused operand
movl r10,36(sp) # save register r10 in unused operand
cvtbl (sp),r10 # get opcode
addl2 $8,r10 # shift negative opcodes
subl3 r10,$EMUTABLE,r11 # forget it if opcode is out of range
movl _emJUMPtable[r10],r10 # call appropriate emulation routine
jsb (r10) # routines put return values into regs 0-5
movl 32(sp),r11 # restore register r11
movl 36(sp),r10 # restore register r10
insv (sp),$0,$4,44(sp) # and condition codes in Opcode spot
addl2 $40,sp # adjust stack for return
addl2 $48,sp # adjust stack for
.word 0xffff # "reserved instruction fault"
.word 0xffff # "reserved instruction fault"