[Project Log] Python on the 6502/C64, 8080, 6800, 6809 and AVR

This code has been previously discussed as troublesome for a different processor:

 05BC BD 1045	      [9] 00920	         jsr    RANDOM    ; Gen random X
 05BF 84 03		      [2] 00921	         anda   #3
 05C1 97 56		      [4] 00922	         staa   ASAVE
 05C3 27 05 (05CA)    [4] 00923	         beq    PUTIN2
 05C5 56		      [2] 00924	PUTIN1   rorb             ; Find X position
 05C6 56		      [2] 00925	         rorb
 05C7 4A		      [2] 00926	         deca
 05C8 26 FB (05C5)    [4] 00927	         bne    PUTIN1
 05CA C5 03		      [2] 00928	PUTIN2   bitb   #3        ; Is position empty?
 05CC 26 DF (05AD)    [4] 00929	         bne    PUTINM    ; If not, repeat
 05CE DA 53		      [3] 00930	         orab   MASK
 05D0 96 56		      [3] 00931	         ldaa   ASAVE
 05D2 27 05 (05D9)    [4] 00932	         beq    PUTIN4
 05D4 59		      [2] 00933	PUTIN3   rolb             ; Put object in map
 05D5 59		      [2] 00934	         rolb
 05D6 4A		      [2] 00935	         deca
 05D7 26 FB (05D4)    [4] 00936	         bne    PUTIN3
 05D9 E7 00		      [6] 00937	PUTIN4   stab   0,X       ; Save it
 05DB 7A 0030	      [6] 00938	         dec    COUNT
 05DE 26 CD (05AD)    [4] 00939	         bne    PUTINM
 05E0 96 53		      [3] 00940	         ldaa   MASK
 05E2 81 03		      [2] 00941	         cmpa   #3        ; Was object a base?
 05E4 26 0E (05F4)    [4] 00942	         bne    PUTIN6
 05E6 D6 5A		      [3] 00943	         ldab   TSAVE1
 05E8 0C		      [2] 00944	         clc
 05E9 56		      [2] 00945	         rorb             ; Find position
 05EA 96 56		      [3] 00946	         ldaa   ASAVE
 05EC 24 02 (05F0)    [4] 00947	         bcc    PUTIN5
 05EE 8B 04		      [2] 00948	         adda   #4
 05F0 97 23		      [4] 00949	PUTIN5   staa   BASESX    ; Save X position

This is the 68000 version:

  600 T  00000920 61000D6A                     bsr     RANDOM                  ; Gen random X
  601 T  00000924 02000003                     andi.b  #3,D0
  602 T  00000928 13C0000017E4                 move.b  D0,ASAVE
  603 T  0000092E 6700000E                     beq     PUTIN2
  604 T  00000932 E20A                 PUTIN1: lsr.b   #1,D2                   ; Recover X flag
  605 T  00000934 E411                         roxr.b  #2,D1                   ; Find X position
  606 T  00000936 E30A                         lsl.b   #1,D2                   ; Save X flag
  607 T  00000938 5300                         subq.b  #1,D0
  608 T  0000093A 6600FFF6                     bne     PUTIN1
  609 T  0000093E 1601                 PUTIN2: move.b  D1,D3
  610 T  00000940 02030003                     andi.b  #3,D3                   ; Is position empty?
  611 T  00000944 6600FFC0                     bne     PUTINM                  ; If not, repeat
  612 T  00000948 8239000017E1                 or.b    MASK,D1
  613 T  0000094E 1039000017E4                 move.b  ASAVE,D0
  614 T  00000954 6700000E                     beq     PUTIN4
  615 T  00000958 E20A                 PUTIN3: lsr.b   #1,D2                   ; Recover X flag
  616 T  0000095A E511                         roxl.b  #2,D1                   ; Put object in map
  617 T  0000095C E30A                         lsl.b   #1,D2                   ; Save X flag
  618 T  0000095E 5300                         subq.b  #1,D0
  619 T  00000960 6600FFF6                     bne     PUTIN3
  620 T  00000964 1681                 PUTIN4: move.b  D1,(A3)                 ; Save it
  621 T  00000966 5339000017CE                 subq.b  #1,COUNT
  622 T  0000096C 6600FF98                     bne     PUTINM
  623 T  00000970 1039000017E1                 move.b  MASK,D0
  624 T  00000976 0C000003                     cmpi.b  #3,D0                   ; Was object a base?
  625 T  0000097A 66000026                     bne     PUTIN6
  626 T  0000097E 1239000017E6                 move.b  TSAVE1,D1
  627 T  00000984 023C00FE                     andi    #$FE,CCR
  628 T  00000988 E211                         roxr.b  #1,D1                   ; Find position
  629 T  0000098A 1039000017E4                 move.b  ASAVE,D0
  630 T  00000990 64000004                     bcc     PUTIN5
  631 T  00000994 5800                         addq.b  #4,D0
  632 T  00000996 13C0000017C1         PUTIN5: move.b  D0,BASESX               ; Save X position

Here is another one which had not been trouble before, but does not like the lack of non-carrying increment and decrement:

 0A4D CE 00B1	      [3] 01496	KILALK   ldx    #SECMAP   ; Remove K's from Q
 0A50 86 10		      [2] 01497	         ldaa   #16
 0A52 97 30		      [4] 01498	         staa   COUNT     ; Setup count
 0A54 C6 04		      [2] 01499	KILAL1   ldab   #4
 0A56 A6 00		      [5] 01500	         ldaa   0,X       ; Find all Klingons
 0A58 46		      [2] 01501	KILAL2   rora
 0A59 25 12 (0A6D)    [4] 01502	         bcs    KILAL4
 0A5B 46		      [2] 01503	         rora
 0A5C 24 01 (0A5F)    [4] 01504	         bcc    KILAL3
 0A5E 0C		      [2] 01505	         clc              ; Clear out
 0A5F 5A		      [2] 01506	KILAL3   decb
 0A60 26 F6 (0A58)    [4] 01507	         bne    KILAL2
 0A62 46		      [2] 01508	         rora
 0A63 A7 00		      [6] 01509	         staa   0,X
 0A65 08		      [4] 01510	         inx
 0A66 7A 0030	      [6] 01511	         dec    COUNT     ; If not done rpt
 0A69 26 E9 (0A54)    [4] 01512	         bne    KILAL1
 0A6B 20 13 (0A80)    [4] 01513	         bra    PHASR6
 0A6D 46		      [2] 01514	KILAL4   rora
 0A6E 20 EF (0A5F)    [4] 01515	         bra    KILAL3
 0A70 DE 40		      [4] 01516	PHASR5   ldx    PHSENG

and the 68000 version

 1173 T  00001210 207C00001837         KILALK: movea.l #SECMAP,A0              ; Remove K's from Q
 1174 T  00001216 103C0010                     move.b  #16,D0
 1175 T  0000121A 13C0000017CE                 move.b  D0,COUNT                ; Setup count
 1176 T  00001220 123C0004             KILAL1: move.b  #4,D1
 1177 T  00001224 1010                         move.b  (A0),D0                 ; Find all Klingons
 1178 T  00001226 E20A                 KILAL2: lsr.b   #1,D2                   ; Recover X flag
 1179 T  00001228 E210                         roxr.b  #1,D0
 1180 T  0000122A 65000028                     bcs     KILAL4
 1181 T  0000122E E210                         roxr.b  #1,D0
 1182 T  00001230 64000006                     bcc     KILAL3
 1183 T  00001234 023C00FE                     andi    #$FE,CCR                ; Clear out
 1184 T  00001238 E30A                 KILAL3: lsl.b   #1,D2                   ; Save X flag
 1185 T  0000123A 5301                         subq.b  #1,D1
 1186 T  0000123C 6600FFE8                     bne     KILAL2
 1187 T  00001240 E20A                         lsr.b   #1,D2                   ; Recover X flag
 1188 T  00001242 E210                         roxr.b  #1,D0
 1189 T  00001244 10C0                         move.b  D0,(A0)+
 1190 T  00001246 5339000017CE                 subq.b  #1,COUNT                ; If not done rpt
 1191 T  0000124C 6600FFD2                     bne     KILAL1
 1192 T  00001250 60000028                     bra     PHASR6
 1193 T  00001254 E210                 KILAL4: roxr.b  #1,D0
 1194 T  00001256 6000FFE0                     bra     KILAL3
 1195 T  0000125A 3039000017B8         PHASR5: move.w  PHSENG,D0
1 Like

As I work with more large large integers, I am curious how my emulators compare with the real hardware.

So I wrote a subroutine to use up 1 million CPU cycles. A 6502 clocked at 1 MHz should take one second to run it.

Python defines a sleep function to pause a program for N seconds. This code is not very useful for that because the clock speeds vary all over the place among the several systems using it.

 0011					  00018	Second:
 						  00019	; Expend 1000000 (6 + 2 + 3 * 3 + 3 * 329988 + 3 * 2582 + 3 * 751 +
 						  00020	;		3 * 2 + 2 * 3 + 2 + 6) cycles
 						  00021	; 6 cycles for jsr in and 6 for rts out
 						  00022
 0011 A2 03		      [2] 00023		ldx	#3
 						  00024
 0013					  00025	Time0:
 0013 A5 00		      [3] 00026		lda	Count			; Expend 3 cycles
 						  00027
 						  00028		; Expend 329988 (2 + 3 + 256 * 1281 + 256 * 5 + 255 * 3 + 2) cycles
 0015 A9 00		      [2] 00029			lda	#0
 0017 85 00		      [3] 00030			sta	Count
 						  00031
 0019					  00032		Time1:
 						  00033
 						  00034			; Expend 1281 (2 + 256 * 2 + 255 * 3 + 2) cycles
 0019 A0 00		      [2] 00035				ldy	#0
 						  00036
 001B					  00037			Time2:
 001B 88		      [2] 00038				dey
 001C D0 FD (001B)  [2/3] 00039				bne	Time2
 						  00040			;
 						  00041
 001E C6 00		      [5] 00042			dec	Count
 0020 D0 F7 (0019)  [2/3] 00043			bne	Time1
 						  00044		;
 						  00045
 						  00046		; Expend 2582 (2 + 3 + 2 * 1281 + 2 * 5 + 1 * 3 + 2) cycles
 0022 A9 02		      [2] 00047			lda	#2
 0024 85 00		      [3] 00048			sta	Count
 						  00049
 0026					  00050		Time1b:
 						  00051
 						  00052			; Expend 1281 (2 + 256 * 2 + 255 * 3 + 2) cycles
 0026 A0 00		      [2] 00053				ldy	#0
 						  00054
 0028					  00055			Time2b:
 0028 88		      [2] 00056				dey
 0029 D0 FD (0028)  [2/3] 00057				bne	Time2b
 						  00058			;
 						  00059
 002B C6 00		      [5] 00060			dec	Count
 002D D0 F7 (0026)  [2/3] 00061			bne	Time1b
 						  00062		;
 						  00063
 						  00064		; Expend 751 (2 + 150 * 2 + 149 * 3 + 2) cycles
 002F A0 96		      [2] 00065			ldy	#150
 						  00066
 0031					  00067		Time1c:
 0031 88		      [2] 00068			dey
 0032 D0 FD (0031)  [2/3] 00069			bne	Time1c
 						  00070		;
 0034 CA		      [2] 00071		dex
 0035 D0 DC (0013)  [2/3] 00072		bne	Time0
 						  00073	;
 						  00074
 0037 60		      [6] 00075		rts
2 Likes

Due to some arm twisting from Paul @Urbite, I am exploring implementing a Space Voyage clone for the TMS9900.

Since I have been doing comparative CPU architectures, these are my initial impressions about this processor.

For good and bad, the designers of the 9900 must have felt that through the competition was 8-bit, they were 16-bit and we had better not forget that.

First the good.

There are sixteen 16-bit registers. With one exception, they are equal in capability.

There are a few simple but very usable addressing modes.

  • Register.

  • Register indirect. The register contains the address of the operand in memory.

  • Register indirect with automatic post-increment. One is added to the register following a byte-sized operation; two after a word-sized operation.

  • Symbolic. The address of a location in memory is specified.

  • Symbolic with register indexing. Because of how instructions are encoded, R0 cannot be used for indexing; a zero for the register means no indexing. With a 16-bit offset value, indexing may be used in two ways:

  1. A constant offset into an array or other data structure whose address is in a register.

  2. A register contains an offset into an array or other data structure in memory.

Many instructions are two-operand to the extreme. Two memory locations may participate without having to load one of them into a register first.

The general purpose registers are not built into the processor, but reside in memory. A special register called the Workspace Pointer contains the address of this array of sixteen words. This register can be quickly reloaded to make a different ā€œset of registersā€ available. A register may be loaded with the address of the previous workspace to provide easy access to the prior context.

And now the bad and the ugly.

  • There is no stack. A subroutine call is made with the BL (Branch and Link) instruction; the return address is placed into R11. Returning from the subroutine is by branching to the address in R11 (B *R11). Not a problem unless subroutines are nested. The saving grace is that a stack can be easily implemented with a few simple instructionsā€¦

  • The designers appear to have only begrudgingly added some single-byte operations. Not many operations have single-byte forms. Words are stored in memory in a big-endian manner, meaning that their most significant byte comes first. A byte operation on memory involves the first or ā€œupperā€ byte at an address as expected, but surprisingly, a byte operation on a register also uses the upper byte of a register. But wait; thereā€™s moreā€¦ There is no byte oriented compare immediate instruction. A program to search a string for a character must either preload that character into another register for use with the compare byte instruction or load characters (with move byte) from the string into the upper half of a register, ensuring that the lower half remains zero, and comparing that with a word whose value is the character to be found times 256.

  • There are no add with carry or subtract with borrow operations. This seems to be indicative of a belief that entities larger than sixteen bits are not needed.

  • There is no direct way to shift the carry flag into a value, making multiple-precision shifts cumbersome.

  • OR or AND operations must involve a constant in the form of OR immediate or AND immediate. Paul @urbite has since informed me that a general purpose OR instruction is masquerading as Set Ones Corresponding (SOC). Why in the world would they call it that? Perhaps for consistancy with Set Zeros Corresponding which is essentially an AND with the inverse of another value. I was afraid I was going to have to resort to self-modifying code to get around this limitation.

  • On the other hand, there is an XOR instruction with a register interacting with a general operand, but not an immediate constant.

Space Voyage was originally written for an 8-bit machine. Transcribing to the 9900 will be awkward in some ways. The first step is converting byte variables, where possible, to word variables so that they may be manipulated natively.

Code to perform multiple-precision addition on the 9900:

 					  00001	*
 					  00002	* R1 = address of one addend
 					  00003	* R2 = address of second addend
 					  00004	* R3 = address of sum
 					  00005	* R4 = number of words in numbers
 					  00006	*
 0000				  00007	BigAdd
 0000 04C5			  00008		clr	R5			; Clear "carry"
 0002 C031			  00009		mov	*R1+,R0		; Get a word of addend 1
 0004 1007 (0014)	  00010		jmp	AddStage
 0006				  00011	AddLoop
 0006 C185			  00012		mov	R5,R6		; Save "carry"
 0008 04C5			  00013		clr	R5			; Clear next "carry"
 000A C031			  00014		mov	*R1+,R0		; Get a word of addend 1
 000C A006			  00015		a	R6,R0		; Add "carry" from previous word
 000E 1702 (0014)	  00016		jnc	AddStage	; Skip if no carry
 0010 0265 0001		  00017		ori	R5,1		; Set "carry"
 0014				  00018	AddStage
 0014 A032			  00019		a	*R2+,R0		; Get a word of addend 2
 0016 CCC0			  00020		mov	R0,*R3+		; Store a word of sum
 0018 1702 (001E)	  00021		jnc	NoCarry2	; Skip if no carry
 001A 0265 0001		  00022		ori	R5,1		; Set "carry"
 001E				  00023	NoCarry2
 001E 0604			  00024		dec	R4
 0020 16F2 (0006)	  00025		jne	AddLoop

and on the 6502:

 						  00005	;
 						  00006	; One = address of one addend
 						  00007	; Two = address of second addend
 						  00008	; Sum = address of sum
 						  00009	; X = number of bytes in numbers
 						  00010	;
 0003					  00011	BigAdd
 0003 18		      [2] 00012		clc
 0004 A0 00		      [2] 00013		ldy	#0
 0006					  00014	AddLoop
 0006 B1 00		    [5/6] 00015		lda	(One),Y
 0008 71 01		    [5/6] 00016		adc	(Two),Y
 000A 91 02		      [6] 00017		sta	(Sum),Y
 000C C8		      [2] 00018		iny
 000D CA		      [2] 00019		dex
 000E D0 F6 (0006)  [2/3] 00020		bne	AddLoop

Code to perform a multiple-precision left shift on the 9900:

 					  00029	*
 					  00030	* R1 = address of number to shift
 					  00031	* R2 = number of words in number
 					  00032	*
 0022				  00033	BigSHL
 0022 04C3			  00034		clr	R3			; Clear "carry"
 0024				  00035	SHLLoop
 0024 C103			  00036		mov	R3,R4		; Save "carry"
 0026 04C3			  00037		clr	R3			; Clear next "carry"
 0028 C011			  00038		mov	*R1,R0		; Get word of number
 002A 0A10			  00039		sla	R0,1		; Shift left one bit
 002C 1701 (0030)	  00040		jnc	NoCarry		; Skip if no carry
 002E 0583			  00041		inc	R3			; Set next "carry"
 0030				  00042	NoCarry
 0030 A004			  00043		a	R4,R0		; Combine with previous "carry"
 0032 CC40			  00044		mov	R0,*R1+		; Put into result
 0034 0602			  00045		dec	R2
 0036 16F6 (0024)	  00046		jne	SHLLoop

and on the 6502:

 						  00024	;
 						  00025	; One = address of number to shift
 						  00026	; X = number of bytes in number
 						  00027	;
 0010					  00028	BigSHL
 0010 18		      [2] 00029		clc
 0011 A0 00		      [2] 00030		ldy	#0
 0013					  00031	SHLLoop
 0013 B1 00		    [5/6] 00032		lda	(One),Y
 0015 2A		      [2] 00033		rol	A
 0016 91 00		      [6] 00034		sta	(One),Y
 0018 C8		      [2] 00035		iny
 0019 CA		      [2] 00036		dex
 001A D0 F7 (0013)  [2/3] 00037		bne	SHLLoop
3 Likes

Not sure if call it arm twisting. Maybe persistent nudging :slight_smile:

2 Likes

Donā€™t forget to the try using the Execute Register instruction of the 9900. You point to the Register that contains the next op code that you stored there. Use this for creating programmable jump instructions. Nothing like using the same instruction that does something different every time you use it. Itā€™s a weird instruction but very powerful if used correctly. Self modifying codeā€¦gotta love it!

2 Likes

Rich - you are hereby awared the ā€˜9900 guruā€™ badge :nerd_face: for knowing this obscure instruction :sunglasses:

Actually, the X instruction isnā€™t limited to a register. All of the four addressing modes described in detail a few posts back are available for the X instruction.

  • register: Rx
  • register indirect: *Rx
  • absolute: @foo
  • register indexed: @foo(Rx)
2 Likes

In my day I wrote a lot of 9900 assembly language. Even modified Power Basic for the 99/4 computer. I turned the joystick connector into a serial printer port and added the Basic commands to interface with it. The $5 external modification was cheaper than the $250 serial interface that TI sold. Even had an EPROM cartridge to store my programs in. Those were fun days back then.

3 Likes

We must have been living in parallel universes back then. Hereā€™s my version of a serial interface through the joystick port. You were a step ahead, as I didnā€™t add the BASIC commands. Instead, one had to interface to it via assembly language. The example code uses the Mini Memory module.

http://www.mainbyte.com/ti99/joytalk/joytalk.pdf

3 Likes

One has to wonder whether TI was trying to create a smaller version of an IBM mainframe.

Similar features:

  • Sixteen general purpose registers
  • No hardware stack
  • Subroutine call by branch and link and a return by branching to an address in a register
  • An ā€œexecuteā€ instruction http://www.simotime.com/asmins01.htm#EX

This program is easy to write in Python:

number = 1
power=0
string=""

while string.find("777777") < 0:

    number = number * 7
    power = power + 1
    string = str(number)

    print('7 ^', power, 'is', string)

The challenge is compiling it to run on a 99/4aā€¦

Overlapping General Purpose Registers in the 9900:

The Sixteen general purposes registers for the 9900 were in memory, not in the CPU. So the registers could be located anywhere in available memory. You could even overlap the register assignments. Such as; before a subroutine you were using registers R13, R14, and R15. During the start of the subroutine, you could define the new set of 16 general purpose registers with an offset of 8 addresses as an example. Then the old R13 register is now R6, old R14 is now R7, and old R15 is now R8. The subroutine now uses R6, R7, and R8 without having to move the contents of any registers. Then after the subroutine you revert back to R13, R14, and R15 with your results. Confusing, but if well documented with comments in your code it worked very well.

Back in the day when memory was expensive, these tricks were necessary due to memory cost. In Assembly language this trick saved a total of 6 Move instruction cycles and increased the speed of your program.

3 Likes

I have been reading about multiply and divide. Both use register pairs. If R15 is specified, you get a ā€œfreeā€ seventeenth register in the word beyond the workspaceā€¦

1 Like

OK, 9900 assembly language experts,

examining the Branch instruction more closelyā€¦

B @THERE 

means the next instruction executed is at the location denoted by the label ā€œTHERE.ā€

And

B *R11

means the next instruction executed is at the location whose address is in the register R11.

So what does

B R11 ; without the '*'

do?

My initial thought was that was the same as the X (eXecute) instruction. But it is not.

The TI Editor/Assembler yields the following:

so the assembler does not think that B R11 is invalid.

I believe it will branch to the memory location which is R11. In that case, the address of the instruction would be WP+22.

1 Like

Interestingā€¦

I never saw that possibility.

So we can load up instructions into the workspace, then execute them while they are subject to change when registers are modified.

The opening stanzas of Space Voyage 9900ā€¦

 Addr Code		  Line#	  Source Statement

 0000 02E0 014E		  00001	SPAVOY	lwpi	WRKSPC		; Load workspace
 0004 020F 014E		  00002			li		R15,STACK	; Load "stack pointer"
 0008 0201 022C		  00003			li		R1,TITLE
 000C 06A0 00A2		  00004			bl		@PSTRNG		; Output title
 0010 0201 0177		  00005			li		R1,STPSFL
 0014 04C0			  00006	SETUP	clr		R0			; Clear all temp storage
 0016 DC40			  00007			movb	R0,*R1+
 0018 0281 0218		  00008			ci		R1,MOVTBL
 001C 16FB (0014)	  00009			jne		SETUP
 001E 0201 023F		  00010			li		R1,SHTLNG	; Short or long version?
 0022 06A0 00A2		  00011			bl		@PSTRNG
 0026 05A0 016F		  00012	SEED0	inc		@RNDM		; Increment 0th byte of random number seed
 002A 06A0 00C4		  00013			bl		@STAT		;   until user types answer
 002E 13FB (0026)	  00014			jeq		SEED0
 0030 06A0 00EC		  00015			bl		@INCH
 0034 0280 5300		  00016			ci		R0,'S'*256
 0038 1305 (0044)	  00017			jeq		SHORT
 003A 0280 7300		  00018			ci		R0,'s'*256
 003E 1302 (0044)	  00019			jeq		SHORT
 0040 05A0 01AC		  00020			inc		@LENGTH		; If long set flag
 0044 B800 016F		  00021	SHORT	ab		R0,@RNDM	; Add to seed
 0048 0201 0426		  00022			li		R1,INTRO0	; Out password prompt
 004C 06A0 00A2		  00023			bl		@PSTRNG
 0050 0201 01C4		  00024			li		R1,PASWRD	; Get password
 0054 05A0 0171		  00025	SEED1	inc		@RNDM+2		; Increment 1st byte of random number seed
 0058 06A0 00C4		  00026			bl		@STAT		;   until user types answer
 005C 13FB (0054)	  00027			jeq		SEED1
 005E 06A0 00EC		  00028			bl		@INCH
 0062 DC40			  00029			movb	R0,*R1+		; Save it
 0064 B800 0171		  00030			ab		R0,@RNDM+2	; Add to seed
 0068 05A0 0173		  00031	SEED2	inc		@RNDM+4		; Increment 2nd byte of random number seed
 006C 06A0 00C4		  00032			bl		@STAT		;   until user types answer
 0070 13FB (0068)	  00033			jeq		SEED2
 0072 06A0 00EC		  00034			bl		@INCH
 0076 DC40			  00035			movb	R0,*R1+		; Save it
 0078 B800 0173		  00036			ab		R0,@RNDM+4	; Add to seed
 007C 05A0 0175		  00037	SEED3	inc		@RNDM+6		; Increment 3rd byte of random number seed
 0080 06A0 00C4		  00038			bl		@STAT		;   until user types answer
 0084 13FB (007C)	  00039			jeq		SEED3
 0086 06A0 00EC		  00040			bl		@INCH
 008A DC40			  00041			movb	R0,*R1+		; Save it
 008C B800 0175		  00042			ab		R0,@RNDM+6	; Add to seed
 0090 06A0 0102		  00043			bl		@RVERIFY	; Ensure random number seed is valid
 0094 0201 01C8		  00044			li		R1,QUDMAP	; Point to quadrant map
 0098 0203 0040		  00045			li		R3,64
 009C 06A0 00F0		  00046	SETUP0	bl		@RANDOM		; Setup number of Klingons

SV99

Notice the code peeking through the registers until the workspace pointer is loaded.

The last two instructions ā€œpushā€ the return address in R11 onto the ā€œstack.ā€

A later

	mov	*R15+,R11

recovers the return address.

3 Likes

Another question for 9900 assembly language expertsā€¦

The flags in the status register of the 9900 appear to be wildly different from other processors.

The L> flag supposedly indicates when an unsigned quantity is greater than zero.

The A> flag supposedly indicates when a signed quantity is greater than zero.

From page 3-29 of

http://www.bitsavers.org/pdf/ti/990/tx990/0943441-9701_990_Assembly_Language_Programming_Guide_Oct78.pdf

The JL (jump if lower) instruction branches when L> is clear and EQ is clear. That makes sense.

The JHE (jump if higher or equal) instruction branches when L> is set or EQ is set. That makes sense.

The JH (jump if higher) instruction branches when L> is set and EQ is clear. The JLE (jump if lower or equal) instruction branches when L> is clear or EQ is set. These two seem to imply that the L> flag is actually set for L>=

Anybody know about this one?

An interesting readā€¦

2 Likes

The pseudo-random number generator for the 9900 is finally working properly.

The algorithm is for 8-bit operations and implementation was tricky on the 9900 which does not provide a full set of 8-bit instructions. Stray bits kept sneaking in through the ā€œunusedā€ halves of 16-bit registers.

3 Likes

The documentation seems to imply that L> and EQ are mutually exclusive: that no value is both greater than 0 and equal to 0.

However, there are a couple of instructions, compare ones corresponding (COC) and compare zeros corresponding (CZC) which affect only the EQ flag. There may be a programming trick lurking in there somewhereā€¦

1 Like