Jump to content
sometimes99er

Bricks (demo)

Recommended Posts

Now Assembly only has 16 Registers and GPL only has 256 bytes of Scratch Pad but the same program in GPL will use less storage and variables then XB or Assembly. Only Forth is more compact, but again much more complicated to learn.

You point out that GPL will use less storage and variables than XB and Assembly. Now you know I'm not an expert at GPL, so I'm not going to try and dissect the GPL version of the demo (post #7), but could you explain how GPL will end up using less variables with "the same program" in question ?

Share this post


Link to post
Share on other sites
Is there any evidence to suggest that the graphics data in GROM has ever changed location? Can I not just assume a hard-coded address?

 

It's in different places in each GROM set. That's why there's a vectored function in GPL to get the data from, and why I wrote this function in the first place. :)

 

It probably is larger than the GPLLNK, but I never liked the idea of relinquishing control and hoping I get it back. :)

 

Ah! OK, thanks Tursi! :thumbsup:

Share this post


Link to post
Share on other sites

Now Assembly only has 16 Registers and GPL only has 256 bytes of Scratch Pad but the same program in GPL will use less storage and variables then XB or Assembly. Only Forth is more compact, but again much more complicated to learn.

You point out that GPL will use less storage and variables than XB and Assembly. Now you know I'm not an expert at GPL, so I'm not going to try and dissect the GPL version of the demo (post #7), but could you explain how GPL will end up using less variables with "the same program" in question ?

 

I also know nothing about GPL, but I would have thought that the GPL version would use the same number of variables as any other languages. To understand what mean, you have to stop thinking in terms of languages, and think in terms of algorithms.

 

Let's a take a bouncing ball that bounces off the screen edges. The minimum number of variables is 4:

 

Current x coordinate

Current y coordinate

x direction (1 or -1)

y direction (1 or -1)

 

You could *possibly* do it with 3 variables, using a screen address instead of x and y coordiantes, but that just makes screen edge detection more complex.

 

So, GPL, XB, Assembly: The *algorithm* still needs 4 (or 3!) variables. The *language* makes no difference. Therefore I don't really agree that GPL uses less variables for a given job/task. The number of variables is inherent in the algorithm, and once you have broken the algorithm down into its most efficient form, that is how many variables you need, in *any* languge.

 

I do agree that GPL is cool though! I will definatlely try it out! :cool:

Share this post


Link to post
Share on other sites

Now Assembly only has 16 Registers and GPL only has 256 bytes of Scratch Pad but the same program in GPL will use less storage and variables then XB or Assembly. Only Forth is more compact, but again much more complicated to learn.

You point out that GPL will use less storage and variables than XB and Assembly. Now you know I'm not an expert at GPL, so I'm not going to try and dissect the GPL version of the demo (post #7), but could you explain how GPL will end up using less variables with "the same program" in question ?

 

Sure I have been using GPL for sometime now, more than most others. I convert from Basic, XB and Assembly to GPL. Like Assembly I can use a address instead of a variable for storage. Like this snippet of code I just wrote:

CLR @>8374 * KEYBOARD 0

G220 SCAN * GET A KEY

CEQ 13,@K * K=13?

BR G220 * NO, GOTO G220

G240 ALL 32

CALL TIMSU * SET UP TIMER

CLR @B * SPRITE COUNTER

ST 32,@NMS * NUMBER OF MOVING SPRITES

MOVE 1,[email protected],#1 * VDP REGISTER 1

MOVE 128,[email protected],[email protected] * SPRITE ATTRIBUTE TABLE

MOVE 192,[email protected],[email protected] * SPRITE DESCRIPTOR BLOCK

MOVE 128,[email protected],[email protected] * SPRITE VELOCITY TABLE

DST S340,@SOND * SOUND LIST ADDRESS

I/O 0,@SOND * PLAY IT

G350 CALL TIMER * TIMER

DCLR [email protected] * STOP PLAYER SPRITE

SCAN * GET A KEY

CEQ 11,@K * UP?

BR G450 * NO, GOTO G450

DST >FB00,[email protected] * LOAD UP MOTION

B G530 * GOTO D530

G450 CEQ 10,@K * DOWN?

BR G480 * YES, GOTO G480

DST >0500,[email protected] * LOAD DOWN MOTION

B G530 * GOTO G530

G480 CEQ 9,@K * RIGHT?

BR G510 * NO, GOTO G510

DST >0005,[email protected] * LOAD RIGHT MOTION

B G530 * GOTO G530

G510 CEQ 8,@K * LEFT?

BR G530 * NO, GOTO G530

DST >00FB,[email protected] * LOAD LEFT MOTION

G530 DST SAT+4,@FAC * SPRITE ATTRIBUTE +4

DST SVT+4,@FAC2 * SPRITE MOTION =4

G540 DST V*FAC,@ARG2 * ROW:COL #2

DST [email protected],@ARG * ROW:COL #1

CALL TIMER * TIMER

DADD 4,@FAC2 * SVT+4

DADD 4,@FAC * SAT+4

DCHE >0344,@FAC * LAST ADDRESS?

BS G350 * YES, GOTO G350

SUB ARG2,@ARG * ROW#1-ROW#2

CHE 9,@ARG * 9 OR HIGHER?

BS G540 * YES, GOTO G540

DST [email protected],@ARG * ROW:COL #1

SUB @ARG3,@ARG1 * COL#1-COL#2

CHE 9,@ARG1 * 9 OR HIGHER?

BS G540 * YES, GOTO G540

G560 DST S560,@SOND * SOUND DATA ADDRESS

I/O 0,@SOND * PLAY IT

DSUB 4,@FAC * RESET TO ADDRESS

DSUB 4,@FAC2 * RESET TO ADDRESS

G620 DST >C000,V*FAC * STORE OFF SCREEN

DCLR V*FAC2 * ZERO SPRITE VELOCITY

INC @B * B+1

CEQ 16,@B * B=32?

BR G350 * NO, GOTO G350

ST >D0,[email protected] * OFF SCREEN SPRITE LOCATION

This scan keyboard for arrow keys, moves the #1 Sprite accordingly and checks for COINCIDENCE, plays a sound if hit and moves that Sprite off screen and stops velocity, also it has a counter for the number of Spites it has collected. It has 11 variables and looks pretty small for all that.

Share this post


Link to post
Share on other sites

Sure I have been using GPL for sometime now, more than most others. I convert from Basic, XB and Assembly to GPL. Like Assembly I can use a address instead of a variable for storage.

 

That's just semantics! A variable *is* a memory location! Where do you think TI BASIC or XB (or Forth) programs store their variables? They are stored in memory, of course! ;)

Share this post


Link to post
Share on other sites

Sure I have been using GPL for sometime now, more than most others. I convert from Basic, XB and Assembly to GPL. Like Assembly I can use a address instead of a variable for storage.

 

That's just semantics! A variable *is* a memory location! Where do you think TI BASIC or XB (or Forth) programs store their variables? They are stored in memory, of course! ;)

 

 

Correct but Basic and XB can not use the same memory to index like ST V*VARIABLE+2,@VARIABLE(@VARIABLE+6), now Forth that is not a problem.

 

 

I feel like I am in some kind of battle with you. Am I correct?

Share this post


Link to post
Share on other sites

I feel like I am in some kind of battle with you. Am I correct?

 

Not at all - The TI is my hobby, and I don't spend/waste negative energy on my hobby! Hobbies should be fun! This isn't an argument (at least, not for me) - it's merely a discussion. We might both learn new stuff along the way. That's what it's all about! :D <-- big smiley!

Share this post


Link to post
Share on other sites

OK, now the C version (compiled with GCC): :)

 

#define VDP_READ_DATA_REG   (*(volatile char*)0x8800)
#define VDP_WRITE_DATA_REG  (*(volatile char*)0x8C00)
#define VDP_ADDRESS_REG     (*(volatile char*)0x8C02)
#define VDP_READ_FLAG       0x00
#define VDP_WRITE_FLAG      0x40
#define VDP_REG_FLAG        0x80
#define SEED                (*(int*)0x83C0)
#define TMP                 (*(volatile char*)0x8300)

void vdp_copy_from_sys(int index, char* src, int size) {
char* end = src + size;
VDP_ADDRESS_REG = index;
VDP_ADDRESS_REG = (char)(index >>  | VDP_WRITE_FLAG;
while(src < end) VDP_WRITE_DATA_REG = *src++;
}

void vdp_write_reg(int i,char c) {
VDP_ADDRESS_REG=c;
VDP_ADDRESS_REG=i|VDP_REG_FLAG;
}

void interrupts() {
asm("limi 2");
asm("limi 0");
}

/* Does not work, GCC "mpy" bug
char random_byte(char c) {
SEED=SEED*0x6FE5+0x7AB9;
int i=(SEED>>+(SEED<<;
return i%c;
} */

void random_byte() {
asm(
	"li r7,>6FE5\n\t"
	"mpy @>83C0,r7\n\t"
	"ai r8,>7AB9\n\t"
	"mov r8,@>83C0\n\t"
	"clr r7\n\t"
	"swpb r8\n\t"
	"mov @>8300,r6\n\t"
	"sra r6,8\n\t"
	"div r6,r7\n\t"
	"swpb r8\n\t"
	"movb r8,@>8300"
);
}

char vdp_read_byte(int index) {
VDP_ADDRESS_REG = index;
VDP_ADDRESS_REG = (char)(index >>  | VDP_READ_FLAG;
asm("nop");
return VDP_READ_DATA_REG;
}

void shadow(char r,char c,char a) {
char g=vdp_read_byte((int)r*32+c);
if(g<39) {
	g=g|a;
	vdp_copy_from_sys((int)r*32+c,&g,1);
}
}

void main() {
char p1[8]={0x00,0x00,0xC0,0xC0,0x3F,0x3F,0xFF,0xFF};
char p2[12]={0x00,0x00,0x00,0x00,0x00,0x00,
	0xC0,0xC0,0xC0,0xC0,0xC0,0xC0};
char p3[16]={0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x7F,
	0x01,0x03,0x03,0x03,0x03,0x03,0xFF,0xFF};
char col[8]={0x10,0x23,0x45,0x68,0x89,0xAB,0xC2,0xEF};
int a,b;

vdp_write_reg(7,13);

for(b=0;b<=1;b++) {
	for(a=0;a<=3;a++) {
		vdp_copy_from_sys(0x900+a*8+b*32,p1+a*2,2);
		vdp_copy_from_sys(0x902+a*8+b*32,p2+b*6,6);
	}
}
for(a=0;a<=6;a++)vdp_copy_from_sys(0x940+a*64,p3,16);
vdp_copy_from_sys(0x384,col,;

char r,c,i,g;
while(1) {
	interrupts();
	TMP=23;random_byte();r=TMP;
	TMP=30;random_byte();c=TMP;
	g=vdp_read_byte((int)r*32+c);
	if(g<40) {
		g=vdp_read_byte((int)r*32+c+1);
		if(g<40) {
			TMP=7;random_byte();i=TMP*8+40;
			vdp_copy_from_sys((int)r*32+c,&i,1);
			i++;
			vdp_copy_from_sys((int)r*32+c+1,&i,1);
			shadow(r,c+2,4);
			shadow(r+1,c,2);
			shadow(r+1,c+1,3);
			shadow(r+1,c+2,1);
		}
	}
}
}

 

Here's the assembly code generated:

 

pseg
even	

def	vdp_copy_from_sys
vdp_copy_from_sys
a    r2, r3
mov  r1, r4
swpb r4
movb r4, @-29694
ori r1, >4000
movb r1, @-29694
c    r2, r3
jhe  L4
L5
movb *r2+, @-29696
c    r3, r2
jh  L5
L4
b    *r11
even	

def	vdp_write_reg
vdp_write_reg
li   r3, >8C02
movb r2, *r3
ori  r1, >80
swpb r1
movb r1, *r3
b    *r11
even	

def	interrupts
interrupts
* Begin inline assembler code
* 23 "main.c" 1
limi 2
* 24 "main.c" 1
limi 0
* End of inline assembler code
b    *r11
even	

def	random_byte
random_byte
* Begin inline assembler code
* 35 "main.c" 1
li r7,>6FE5
mpy @>83C0,r7
ai r8,>7AB9
mov r8,@>83C0
clr r7
swpb r8
mov @>8300,r6
sra r6,8
div r6,r7
swpb r8
movb r8,@>8300
* End of inline assembler code
b    *r11
even	

def	vdp_read_byte
vdp_read_byte
mov  r1, r3
swpb r3
li   r2, >8C02
movb r3, *r2
movb r1, *r2
* Begin inline assembler code
* 53 "main.c" 1
nop
* End of inline assembler code
movb @-30720, r1
b    *r11
even	

def	shadow
shadow
sra  r2, 8
sra  r1, 8
sla  r1, >5
a    r1, r2
mov  r2, r1
swpb r1
movb r1, @-29694
movb r2, @-29694
* Begin inline assembler code
* 53 "main.c" 1
nop
* End of inline assembler code
movb @-30720, r4
ci   r4, >26FF
jgt  L18
socb r3, r4
movb r1, @-29694
ori r2, >4000
movb r2, @-29694
movb r4, @-29696
L18
b    *r11
even	

def	main
main
ai   r10, >FFC8
mov  r10, r0
mov  r11, *r0+
mov  r9, *r0+
mov  r13, *r0+
mov  r14, *r0+
mov  r15, *r0
mov  r10, r8
inct r8
li   r9, memcpy
mov  r8, r1
li   r2, C.4.1264
li   r3, >8
mov  r8, @54(r10)
bl   *r9
mov  r10, r13
ai   r13, >12
mov  r13, r1
li   r2, C.5.1265
li   r3, >C
bl   *r9
mov  r10, r15
ai   r15, >1E
mov  r15, r1
li   r2, C.6.1266
li   r3, >10
bl   *r9
mov  r10, r14
ai   r14, >A
mov  r14, r1
li   r2, C.7.1267
li   r3, >8
bl   *r9
li   r2, >D87
movb r2, @-29694
swpb r2
movb r2, @-29694
mov  r13, r6
clr  r2
mov  @54(r10), r8
L20
mov  r8, r3
movb r2, r7
clr  r5
movb r2, r0
ai   r0, >200
L23
movb r7, @-29694
li   r1, >4900
movb r1, @-29694
movb *r3, @-29696
movb @1(r3), @-29696
mov  r5, r4
swpb r4
andi r4, >FF00
sla  r4, >3
ab   r0, r4
movb r4, @-29694
li   r1, >4900
movb r1, @-29694
mov  r6, r1
clr  r4
jmp  L21
L22
movb *r1+, @-29696
L21
inc  r4
ci   r4, >7
jne  L22
inc  r5
ai   r7, >800
inct r3
ci   r5, >4
jne  L23
ai   r6, >6
ai   r2, >2000
li   r5, >4000
cb   r2, r5
jne  L20
li   r3, >940
mov  r10, r1
ai   r1, >2E
L27
movb r2, @-29694
mov  r3, r4
ori r4, >4000
movb r4, @-29694
mov  r15, r4
jmp  L25
L26
movb *r4+, @-29696
L25
c    r4, r1
jne  L26
ai   r2, >4000
ai   r3, >40
ci   r3, >B00
jne  L27
li   r1, >8443
movb r1, @-29694
swpb r1
movb r1, @-29694
mov  r14, r2
L28
c    r2, r13
jeq  L37
movb *r2+, @-29696
jmp  L28
L37
li   r14, shadow
L36
* Begin inline assembler code
* 23 "main.c" 1
limi 2
* 24 "main.c" 1
limi 0
* End of inline assembler code
li   r5, >1700
movb r5, @-32000
* Begin inline assembler code
* 35 "main.c" 1
li r7,>6FE5
mpy @>83C0,r7
ai r8,>7AB9
mov r8,@>83C0
clr r7
swpb r8
mov @>8300,r6
sra r6,8
div r6,r7
swpb r8
movb r8,@>8300
* End of inline assembler code
movb @-32000, r13
li   r6, >1E00
movb r6, @-32000
* Begin inline assembler code
* 35 "main.c" 1
li r7,>6FE5
mpy @>83C0,r7
ai r8,>7AB9
mov r8,@>83C0
clr r7
swpb r8
mov @>8300,r6
sra r6,8
div r6,r7
swpb r8
movb r8,@>8300
* End of inline assembler code
movb @-32000, r9
movb r13, r3
sra  r3, 8
sla  r3, >5
movb r9, r1
sra  r1, 8
a    r1, r3
mov  r3, r2
swpb r2
movb r2, @-29694
mov  r3, r1
movb r1, @-29694
* Begin inline assembler code
* 53 "main.c" 1
nop
* End of inline assembler code
movb @-30720, r4
ci   r4, >27FF
jeq  JMP_0
jlt  JMP_0
b    @L36
JMP_0
inc  r3
mov  r3, r4
swpb r4
movb r4, @-29694
movb r3, @-29694
* Begin inline assembler code
* 53 "main.c" 1
nop
* End of inline assembler code
movb @-30720, r5
ci   r5, >27FF
jeq  JMP_1
jlt  JMP_1
b    @L36
JMP_1
li   r5, >700
movb r5, @-32000
* Begin inline assembler code
* 35 "main.c" 1
li r7,>6FE5
mpy @>83C0,r7
ai r8,>7AB9
mov r8,@>83C0
clr r7
swpb r8
mov @>8300,r6
sra r6,8
div r6,r7
swpb r8
movb r8,@>8300
* End of inline assembler code
movb @-32000, r5
sra  r5, 8
ai   r5, >5
swpb r5
andi r5, >FF00
sla  r5, >3
movb r2, @-29694
ori r1, >4000
movb r1, @-29694
movb r5, @-29696
ai   r5, >100
movb r4, @-29694
ori r3, >4000
movb r3, @-29694
movb r5, @-29696
movb r9, r15
ai   r15, >200
movb r13, r1
movb r15, r2
li   r3, >400
bl   *r14
ai   r13, >100
movb r13, r1
movb r9, r2
li   r3, >200
bl   *r14
movb r13, r1
movb r9, r2
ai   r2, >100
li   r3, >300
bl   *r14
movb r13, r1
movb r15, r2
li   r3, >100
bl   *r14
b    @L36
C.7.1267
byte	16
byte	35
byte	69
byte	104
byte	-119
byte	-85
byte	-62
byte	-17
C.6.1266
byte	0
byte	0
byte	0
byte	0
byte	0
byte	0
byte	63
byte	127
byte	1
byte	3
byte	3
byte	3
byte	3
byte	3
byte	-1
byte	-1
C.5.1265
byte	0
byte	0
byte	0
byte	0
byte	0
byte	0
byte	-64
byte	-64
byte	-64
byte	-64
byte	-64
byte	-64
C.4.1264
byte	0
byte	0
byte	-64
byte	-64
byte	63
byte	63
byte	-1
byte	-1

ref	memcpy

 

 

http://www.youtube.com/watch?v=z7lN6QH5m40

Edited by lucien2
  • Like 2

Share this post


Link to post
Share on other sites

If not for Cartidges and that Slot there would be no TurboForth cart.

If not Cartidges and that Slot, then it would probably have had some black boxes and a box port holder or something. I'm sure Willsy would have found a way.

 

:)

Share this post


Link to post
Share on other sites

Thanks. Excellent video. GPL is apparently not worth it, when C is so much easier.

 

Wow! Yes! Awesome! That beats TF, for sure. However, the development cycle in TF is much nicer ;-)

 

Okay, it looks like GCC has set the bar that I have to try to match/beat. This may take some time!

 

Regarding GPL, I think if you can write TMS9900 assembly, you'll have no problems learning GPL. Personally, I'm very interested in learning it - I won't feel like I've fully explored the TI and got the TI coder black belt until I do!

 

Mark

  • Like 1

Share this post


Link to post
Share on other sites

When the TI99 was introduced it was the GROM slot that sold the machine not the DISK programs or TAPE programs.

The TI99 didn't really sell that well when it was introduced. Other home computers was selling with a slot too. That so called GROM slot was a ROM slot too, and thank you Atarisoft for a few decent games for this very closed architecture. I had to buy Mini Memory, just to execute machine code, which most of my friends could do without any expansion. Nice CPU though. Generally a strange architecture. Multiplexer, GROM (slow compared to ROM), and RAM was then actually VRAM.

 

TI Basic was one of the slowest Basics around. A few nice commands, but also many absent. They said it was double interpreted or something. It had to do with GROM and GPL. None of the competitors had that, and I think they did fine without.

 

The TI99 looked cool and nice though, and prices were falling. TI Invaders, Parsec and Speech was hot for a while. But TI was loosing money and shot it down.

 

I don't think it was the GROM slot that sold the machine, I think it was the silver color metal overlay/casing, the cool brand, and Cosby telling us "tons of software". I could also argue that it was the Video Display Processor that sold the machine. Colors and sprites were more convincing than some guy saying, hey you'll get a GROM slot. By the way, nobody in the store told me I was getting a GROM slot, so that was not reason why I bought it. We might as well have to give credit to silicon.

 

:)

Share this post


Link to post
Share on other sites
OK, now the C version (compiled with GCC): :)

 

Thanks for posting that. It's awesome to see the GCC compiler producing programs! And it looks like it runs very well!

Share this post


Link to post
Share on other sites

Regarding GPL, I think if you can write TMS9900 assembly, you'll have no problems learning GPL.

I think so too.

 

The syntax for GPL is very much like XB.

I think the syntax looks more like Assembler.

 

Personally, I'm very interested in learning it - I won't feel like I've fully explored the TI and got the TI coder black belt until I do!

I strongly resent that. Meet me outside.

 

I'm really very interested in GPL and plan on having a serious go with it soon. The thing about assembly language on the TI is the TMS9900 instruction set - assembly language on the 99xx is *so easy*, that once you know it well, it really doesn't make sense to 'go backwards' into other languges.

Yep.

 

:)

Share this post


Link to post
Share on other sites

Here is a straight assembly version designed to be run as a cartridge. I deviated a little, taking advantage of the fact that I was using assembly, and also a requirement of needing the 32K RAM expansion. I use the lower 8K to buffer the screen in CPU RAM instead of reading / writing the VRAM over and over. The display is pretty much instant, and keep in mind that nothing is drawn to the screen until all the bricks are placed.

 

 

 

**
* Bricks with shadows demo
*
      DEF  STDHDR

* VDP Memory Map
*
VDPRD  EQU  >8800             * VDP read data
VDPSTA EQU  >8802             * VDP status
VDPWD  EQU  >8C00             * VDP write data
VDPWA  EQU  >8C02             * VDP set read/write address

* Workspace
WRKSP  EQU  >8300             * Workspace
R0LB   EQU  WRKSP+1           * R0 low byte reqd for VDP routines

PGTB   EQU  >2000             * VRAM Pattern Generator Table Base Address
CTBA   EQU  >0300             * VRAM Color Table Base Address
SBUF   EQU  >2000             * Low 32K RAM expansion for screen buffer


**
* Set Up Cartridge
*
* Always mapped to the cartidge address range
      AORG >6000

STDHDR BYTE >AA               * Indicates a standard header
      BYTE >01               * Version number
      BYTE >01               * Number of programs (optional)
      BYTE >00               * Not used
      DATA >0000             * Pointer to power-up list (can't use in cartridge ROM)
      DATA PROG              * Pointer to program list
      DATA >0000             * Pointer to DSR list
      DATA >0000             * Pointer to subprogram list
      DATA >0000             * Pointer to ISR list
PROG   DATA >0000             * No next menu item
      DATA MAIN              * Program start address for this menu item
      BYTE 10                * Length of text for menu screen
      TEXT 'BRICK DEMO'
      EVEN

**
* Scratch pad RAM use - Variables
*
* *THESE MUST BE IN RAM* (since this is a ROM based game... duh)
*           >8300             * Workspace
*           >831F             * Bottom of workspace
STACK  EQU  >8320             * Subrouting stack, grows down (8 bytes)
*           >8322             * The stack is maintained in R10 and
*           >8324             * supports up to 4 BL calls
*           >8326

NUM38  EQU  >8328             * Number 38 in scratchpad
NUM39  EQU  >8329             * Number 39 in scratchpad
NUM4   EQU  >832A             * Number  4 in scratchpad
NUM3   EQU  >832B             * Number  3 in scratchpad
NUM2   EQU  >832C             * Number  2 in scratchpad
NUM1   EQU  >832D             * Number  1 in scratchpad

* Random Number Memory Map - The console seeds >83C0 at startup, see E/A pg. 406
RAND16 EQU  >83C0             * 16-BIT random number
RANDOM EQU  >83C1             * 8-BIT random number


* Shadow masks
      EVEN
P1     DATA >0000,>0000,>0000,>0000
      DATA >8000,>0000,>0000,>0000
      DATA >3F00,>0000,>0000,>0000
      DATA >FF00,>0000,>0000,>0000
      DATA >0000,>8080,>8080,>8080
      DATA >8000,>8080,>8080,>8080
      DATA >3F00,>8080,>8080,>8080
      DATA >FF00,>8080,>8080,>8080
P1E

BRICK
      DATA >0000,>0000,>0000,>3F7F
      DATA >0103,>0303,>0303,>FFFF
BRICKE

COLOR
      BYTE >23               * Med Grn on Lt Grn
      BYTE >45               * Drk Blu on Lt Blu
      BYTE >68               * Drk Red on Med Red
      BYTE >89               * Med Red on Lt Red
      BYTE >AB               * Drk Yel on Lt Yel
      BYTE >C2               * Drk Grn on Med Grn
      BYTE >EF               * Gray on White
COLORE
      EVEN

RNDCH1 BYTE 40,48,56,64,72,80,88
RNDCH2 BYTE 41,49,57,65,73,81,89
      EVEN

NUM38B BYTE 38                * Number 38 to load to scratchpad
NUM39B BYTE 39                * Number 39 to load to scratchpad
NUM4B  BYTE 4                 * Number  4 to load to scratchpad
NUM3B  BYTE 3                 * Number  3 to load to scratchpad
NUM2B  BYTE 2                 * Number  2 to load to scratchpad
NUM1B  BYTE 1                 * Number  1 to load to scratchpad

      EVEN

*********************************************************************
*
* Main Entry Point
*
*
MAIN
      LIMI 0                 * Prevent the console ISR from running
      LWPI WRKSP

      LI   R10,STACK         * Set up the stack pointer

      MOVB @NUM38B,@NUM38    * Set up constants in scratchpad
      MOVB @NUM39B,@NUM39
      MOVB @NUM4B,@NUM4
      MOVB @NUM3B,@NUM3
      MOVB @NUM2B,@NUM2
      MOVB @NUM1B,@NUM1

      BL   @GMODE            * Set Graphics I

*      Clear the screen
      CLR  R0                * VRAM address >0000
      LI   R1,>2000          * Write character 32 (>20) to all locations
      LI   R2,768            * All screen locations
      BL   @VSMW

*      Clear the CPU RAM screen buffer
      LI   R1,>2020
      LI   R2,766
LP01
      MOV  R1,@SBUF(R2)
      DECT R2
      JNE  LP01
      MOV  R1,@SBUF

*      Define shadow characters
      LI   R0,PGTB+256       * Start at character 32 (32 * 8 bytes per character)
      LI   R1,P1             * Address of source data
      LI   R2,P1E-P1         * Length (in bytes) of data to write
      BL   @VMBW

*      Define brick characters.  The definition
*      is for the dark color of the brick.
      LI   R3,PGTB+320       * Start bricks at character 40 (40 * 
      LI   R4,7              * Write 6 characters sets
LP05
      MOV  R3,R0             * R0 needs to hold the VRAM address maintained by R3
      LI   R1,BRICK          * Address of source data
      LI   R2,BRICKE-BRICK   * Length (in bytes) of data to write
      BL   @VMBW
      AI   R3,64             * Next character set
      DEC  R4                * Decrement counter
      JNE  LP05

*      Set up brick colors.
      LI   R0,CTBA+5         * Character 40 starts in color set 5
      LI   R1,COLOR          * Address of source data
      LI   R2,COLORE-COLOR   * Length (in bytes) of data to write
      BL   @VMBW

* After failing to place a brick X times, stop
      LI   R9,1024
FAIL
      DEC  R9
      JNE  RNDLP
      B    @DUMP

* RandomNumber:
*   R=INT(RND*23)+1 :: C=INT(RND*30)+1
RNDLP
      BL   @RANDNO           * Get a random number (in R5)
      LI   R3,736            * Pick a screen buffer location (no bottom row)
      CLR  R4                * Prepare for division
      DIV  R3,R4             * Get a number between 0 and 735 in R5
      MOV  R5,R6             * Store the random screen location in R6
      ANDI R5,>001F          * Don't use the column 31 or 32 since bricks are 2 wide
      CI   R5,>001F
      JEQ  FAIL
      CI   R5,>001E
      JEQ  FAIL

      CB   @SBUF(R6),@NUM39  * CALL GCHAR(R, C, G)
      JH   FAIL              * IF G>39 THEN RandomNumber

      CB   @SBUF+1(R6),@NUM39 * CALL GCHAR(R, C+1, G)
      JH   FAIL               * IF G>39 THEN RandomNumber

* I=INT(RND*7)*8+40
LP10
      BL   @RANDNO           * Get a random number (in R5)
      LI   R3,7              * Divide by 7
      CLR  R4                * Prepare for division
      DIV  R3,R4             * Get a number between 0 and 6 in R5

      MOVB @RNDCH1(R5),@SBUF(R6)       * CALL HCHAR(R, C, I)
      MOVB @RNDCH2(R5),@SBUF+1(R6)     * CALL HCHAR(R, C+1, I+1)

* CheckRight:
      CB   @SBUF+2(R6),@NUM38          * CALL GCHAR(R, C+2, G)
      JH   SKIP05                      * IF G>38 THEN CheckDown
      SOCB @NUM4,@SBUF+2(R6)           * CALL HCHAR(R, C+2, G OR 4)

* CheckDown:
SKIP05
      CB   @SBUF+32(R6),@NUM38         * CALL GCHAR(R+1, C, G)
      JH   SKIP06                      * IF G>38 THEN CheckCorner
      SOCB @NUM2,@SBUF+32(R6)          * CALL HCHAR(R+1, C, G OR 2)

* CheckCorner:
SKIP06
      CB   @SBUF+33(R6),@NUM38         * CALL GCHAR(R+1, C+1, G)
      JH   SKIP07                      * IF G>38 THEN CheckDiagonal
      SOCB @NUM3,@SBUF+33(R6)          * CALL HCHAR(R+1, C+1, G OR 3)

* CheckDiagonal:
SKIP07
      CB   @SBUF+34(R6),@NUM38         * CALL GCHAR(R+1, C+2, G)
      JH   SKIP08                      * IF G>38 THEN RandomNumber
      SOCB @NUM1,@SBUF+34(R6)          * CALL HCHAR(R+1, C+2, G OR 1)

SKIP08
      B    @RNDLP            * GOTO RandomNumber

* Write screen buffer to VRAM
DUMP
      CLR  R0
      LI   R1,SBUF
      LI   R2,768
      BL   @VMBW

ILOOP
      LIMI 2
      LIMI 0
      JMP  ILOOP


*********************************************************************
*
* Generates a weak pseudo random number and places it in RAND16
*
* R5   - 16-bit random number and stored in RAND16 for next round
*
RMASK  DATA >B400
RANDNO
      MOV  @RAND16,R5        * The seed must not be 0
      JNE  RND01
      INC  R5
RND01
      SRL  R5,1              * Divide by 2
      JNC  RND02             * Jump if no carry
      XOR  @RMASK,R5         * Apply XOR magic with a special mask
RND02
      MOV  R5,@RAND16
      B    *R11
*// RANDNO


*********************************************************************
*
* Sets the graphics mode
*
*                             * Bit value 128 64 32 16  8  4  2  1
*                             * Bit order   0  1  2  3  4  5  6  7
GMODE
      MOV  R11,*R10+         * Push return address onto the stack
      CLR  R0                * M3 is bit 6 and is off for Graphics I
      BL   @VWTR

      LI   R0,>01E0          * 11100000 Graphics I
      BL   @VWTR

      LI   R0,>0200          * Name Base Table to >0000 - >02FF (768 bytes)
      BL   @VWTR

      LI   R0,>030C          * Color Table to >0300 - >0320 (32 bytes)
      BL   @VWTR

      LI   R0,>0404          * Pattern Generator Table to >2000 - >2800 (2048 bytes)
      BL   @VWTR

      LI   R0,>0507          * Sprite Attribute Table to >0380 - >03FF (128 bytes)
      BL   @VWTR

      LI   R0,>0605          * Sprite Pattern Table to >2800 - >2C00 (1024 bytes)
      BL   @VWTR

      LI   R0,>0380          * Disable all sprite processing by writing >D0 (208)
      LI   R1,>D000          * to the vertical position of the first sprite entry
      BL   @VSBW

*      Set colors
      LI   R0,>071D          * R7 is the text-mode text color and the border color
      BL   @VWTR

      LI   R0,>0300          * Start of color table
      LI   R1,>1D00          * Black on magenta
      LI   R2,>0020          * All color table entries
      BL   @VSMW

      DECT R10               * Pop return address off the stack
      MOV  *R10,R11
      B    *R11
*// GMODE


*********************************************************************
*
* VDP Single Byte Write
*
* R0   Write address in VDP RAM
* R1   MSB of R1 sent to VDP RAM
*
* R0 is modified, but can be restored with: ANDI R0,>3FFF
*
VSBW   MOVB @R0LB,@VDPWA      * Send low byte of VDP RAM write address
      ORI  R0,>4000          * Set read/write bits 14 and 15 to write (01)
      MOVB R0,@VDPWA         * Send high byte of VDP RAM write address
      MOVB R1,@VDPWD         * Write byte to VDP RAM
      B    *R11
*// VSBW


*********************************************************************
*
* VDP Single Byte Multiple Write
*
* R0   Starting write address in VDP RAM
* R1   MSB of R1 sent to VDP RAM
* R2   Number of times to write the MSB byte of R1 to VDP RAM
*
* R0 is modified, but can be restored with: ANDI R0,>3FFF
*
VSMW   MOVB @R0LB,@VDPWA      * Send low byte of VDP RAM write address
      ORI  R0,>4000          * Set read/write bits 14 and 15 to write (01)
      MOVB R0,@VDPWA         * Send high byte of VDP RAM write address
VSMWLP MOVB R1,@VDPWD         * Write byte to VDP RAM
      DEC  R2                * Byte counter
      JNE  VSMWLP            * Check if done
      B    *R11
*// VSMW


*********************************************************************
*
* VDP Multiple Byte Write
*
* R0   Starting write address in VDP RAM
* R1   Starting read address in CPU RAM
* R2   Number of bytes to send to the VDP RAM
*
* R0 is modified, but can be restored with: ANDI R0,>3FFF
*
VMBW   MOVB @R0LB,@VDPWA      * Send low byte of VDP RAM write address
      ORI  R0,>4000          * Set read/write bits 14 and 15 to write (01)
      MOVB R0,@VDPWA         * Send high byte of VDP RAM write address
VMBWLP MOVB *R1+,@VDPWD       * Write byte to VDP RAM
      DEC  R2                * Byte counter
      JNE  VMBWLP            * Check if done
      B    *R11
*// VMBW


*********************************************************************
*
* VDP Single Byte Read
*
* R0   Read address in VDP RAM
* R1   MSB of R1 set to byte from VDP RAM
*
VSBR   MOVB @R0LB,@VDPWA      * Send low byte of VDP RAM write address
      MOVB R0,@VDPWA         * Send high byte of VDP RAM write address
      MOVB @VDPRD,R1         * Read byte from VDP RAM
      B    *R11
*// VSBR


*********************************************************************
*
* VDP Multiple Byte Read
*
* R0   Starting read address in VDP RAM
* R1   Starting write address in CPU RAM
* R2   Number of bytes to read from VDP RAM
*
VMBR   MOVB @R0LB,@VDPWA      * Send low byte of VDP RAM write address
      MOVB R0,@VDPWA         * Send high byte of VDP RAM write address
VMBRLP MOVB @VDPRD,*R1+       * Read byte from VDP RAM
      DEC  R2                * Byte counter
      JNE  VMBRLP            * Check if finished
      B    *R11
*// VMBR


*********************************************************************
*
* VDP Write To Register
*
* R0 MSB    VDP register to write to
* R0 LSB    Value to write
*
VWTR   MOVB @R0LB,@VDPWA      * Send low byte (value) to write to VDP register
      ORI  R0,>8000          * Set up a VDP register write operation (10)
      MOVB R0,@VDPWA         * Send high byte (address) of VDP register
      B    *R11
*// VWTR

      END

 

 

  • Like 1

Share this post


Link to post
Share on other sites

Thank you Matthew, that is absolutely marvellous. Yes, almost instant. Very nice twist - the approach. Use of white space is of cause important (readability), still the original XB source is pretty compact for what it is. 18 lines which might even be brought down further. Maybe at the expensive of speed, but then XB can't hardly draw two bricks before your version fills the screen.

 

:thumbsup:

Share this post


Link to post
Share on other sites

/* Does not work, GCC "mpy" bug
char random_byte(char c) {
SEED=SEED*0x6FE5+0x7AB9;
int i=(SEED>>+(SEED<<;
return i%c;
} */

 

Thanks to insomnia, it's already corrected. So here's the new version, without this awful inline assembly trick.

 

#define VDP_READ_DATA_REG   (*(volatile char*)0x8800)
#define VDP_WRITE_DATA_REG  (*(volatile char*)0x8C00)
#define VDP_ADDRESS_REG     (*(volatile char*)0x8C02)
#define VDP_READ_FLAG       0x00
#define VDP_WRITE_FLAG      0x40
#define VDP_REG_FLAG        0x80
#define SEED                (*(int*)0x83C0)

void vdp_copy_from_sys(int index, char* src, int size) {
char* end = src + size;
VDP_ADDRESS_REG = index;
VDP_ADDRESS_REG = (char)(index >>  | VDP_WRITE_FLAG;
while(src < end) VDP_WRITE_DATA_REG = *src++;
}

void vdp_write_reg(int i,char c) {
VDP_ADDRESS_REG=c;
VDP_ADDRESS_REG=i|VDP_REG_FLAG;
}

void interrupts() {
asm("limi 2");
asm("limi 0");
}

char random_byte(char c) {
SEED=SEED*0x6FE5+0x7AB9;
int i=((unsigned)SEED>>+(SEED<<;
return (unsigned)i%c;
}


char vdp_read_byte(int index) {
VDP_ADDRESS_REG = index;
VDP_ADDRESS_REG = (char)(index >>  | VDP_READ_FLAG;
asm("nop");
return VDP_READ_DATA_REG;
}

void shadow(char r,char c,char a) {
char g=vdp_read_byte((int)r*32+c);
if(g<39) {
	g=g|a;
	vdp_copy_from_sys((int)r*32+c,&g,1);
}
}

void main() {
char p1[8]={0x00,0x00,0xC0,0xC0,0x3F,0x3F,0xFF,0xFF};
char p2[12]={0x00,0x00,0x00,0x00,0x00,0x00,
	0xC0,0xC0,0xC0,0xC0,0xC0,0xC0};
char p3[16]={0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x7F,
	0x01,0x03,0x03,0x03,0x03,0x03,0xFF,0xFF};
char col[8]={0x10,0x23,0x45,0x68,0x89,0xAB,0xC2,0xEF};
int a,b;

vdp_write_reg(7,13);

for(b=0;b<=1;b++) {
	for(a=0;a<=3;a++) {
		vdp_copy_from_sys(0x900+a*8+b*32,p1+a*2,2);
		vdp_copy_from_sys(0x902+a*8+b*32,p2+b*6,6);
	}
}
for(a=0;a<=6;a++)vdp_copy_from_sys(0x940+a*64,p3,16);
vdp_copy_from_sys(0x384,col,;

char r,c,i,g;
while(1) {
	interrupts();
	r=random_byte(23);
	c=random_byte(30);
	g=vdp_read_byte((int)r*32+c);
	if(g<40) {
		g=vdp_read_byte((int)r*32+c+1);
		if(g<40) {
			i=random_byte(7)*8+40;
			vdp_copy_from_sys((int)r*32+c,&i,1);
			i++;
			vdp_copy_from_sys((int)r*32+c+1,&i,1);
			shadow(r,c+2,4);
			shadow(r+1,c,2);
			shadow(r+1,c+1,3);
			shadow(r+1,c+2,1);
		}
	}
}
}

  • Like 1

Share this post


Link to post
Share on other sites

Thank you Matthew, that is absolutely marvellous. Yes, almost instant. Very nice twist - the approach.

 

It could be faster if there was an actual "end condition", all of the versions could really. Filling the screen randomly is easy, but inefficient. I think once you get to a certain density, linear scanning to fill in the holes would probably speed things up. I was going to do that, but it was late. ;-)

 

In my assembly version, the screen is not drawn until trying to place a brick has failed 1024 times, which is a completely arbitrary number that I chose based on trial and error. If you reduce the number you get results faster, but less dense. However, as the number goes up, you get less and less for a bigger and bigger number as the random number selection tries to fill out those last few spots.

 

still the original XB source is pretty compact for what it is. 18 lines which might even be brought down further. Maybe at the expensive of speed, but then XB can't hardly draw two bricks before your version fills the screen.

 

Absolutely, the XB version is compact! That's the advantage of a higher level language, and that should always be considered. Unfortunately for us, using XB means we greatly sacrifice speed. The TF and C options seem to produce something in the middle are nice alternatives to have. For me though, I'll always be an assembly guy. Also keep in mind though, a good part of the code is support functions, cartridge header setup, and EQUates which don't resolve in to actual code. But, the program listing as a whole is still the largest version I think, and that probably makes it look intimidating.

  • Like 1

Share this post


Link to post
Share on other sites

Matthew I was thinking, about what would the calculation be for filling the screen.

 

There would be a range of like 300 at the least and 320 at the most in the range so you would not have to do any check for being done till that lowest range is reached.

 

Even in XB that would be a section of program that would not slow down the main loop.

 

Like:

 

FOR X=1 TO LOWESTRANGE

NEXT X

Then start checking if done.

Edited by RXB

Share this post


Link to post
Share on other sites

There would be a range of like 300 at the least and 320 at the most in the range so you would not have to do any check for being done till that lowest range is reached.

 

Let's number rows, 1 to 24 and columns, 1 to 32 like XB does. When placing bricks, row 24 and column 32 are always reserved for shadows. Shadows anywhere else run the risk of being overwritten by a brick.

 

If we look at the rows only, we can place bricks in any of the first 23 rows - last one is for shadows only.

 

When we look at the columns only, we have 31 columns at our disposal - again last one for shadows only. Bricks are 2 columns wide, so the maximum number of bricks per row is 31/2 = 15.5, that's obviously a maximum 15 bricks (shown below). Now there may be a space between bricks with the width of 1 column (a width of 2 would leave room for a brick). If we place the first brick at column 2, we see that a space and a brick fills 3 columns. We could continue like that to the right of this, putting a space and a brick. The minimum number of bricks per row is 31/3 = 10.333, and that's 10 bricks.

 

bricks0002.png

 

Overall we multiply the minimum and maximum numbers, 10 and 15, with the available number of rows, 23. Minimum number of bricks for the screen is 230 (10*23) and maximum is 345 (15*23).

 

icon_smile.gif

Edited by sometimes99er

Share this post


Link to post
Share on other sites

That is a much wider spread then I originally thought there would be.

Share this post


Link to post
Share on other sites

Now here's the TP99 version. As explained in the doc, you have to run from DSK1 and put RUNLIB in the disk.

 

I didn't find it in the doc, but you also have to run first TP99 or LK99 and do a warm restart... I edited LK99 to just load and restart, and renamed it LD99.

 

Edit: LD99 is not needed with the hardware.

 

PROGRAM BRICKS;

CONST
   PAT=2048;
   SCR=4096;
   COL=896;
VAR
   P1:ARRAY[3] OF STRING[4];
   P2:ARRAY[1] OF STRING[12];
   A,B,R,C,I,G:INTEGER;

PROCEDURE PEEKV(ADDR:INTEGER; VAR BYTE: STRING[1]); EXTERNAL;
PROCEDURE POKEV(ADDR:INTEGER; BYTE: STRING[1]); EXTERNAL;
PROCEDURE BREAK; EXTERNAL;
FUNCTION BWOR(I1,I2:INTEGER):INTEGER; EXTERNAL;

PROCEDURE CHAR(C:INTEGER; S:STRING[16]);
VAR
   I,I2,I3:INTEGER;
   B:BOOLEAN;
   S2:STRING[1];
BEGIN
   B:=FALSE;
   FOR I:=1 TO 16 DO BEGIN
       I2:=ASC(SEG(S,I,1));
       IF I2>57 THEN I2:=I2-7;
       I2:=I2-48;
       IF B THEN BEGIN
           B:=FALSE;
           I3:=I3+I2;
           S2:=CHR(I3);
           POKEV(PAT+C*8+I DIV 2-1,S2);
       END
       ELSE BEGIN
           B:=TRUE;
           I3:=I2*16;
       END;
   END;
END;

PROCEDURE COLOR(I,F,B:INTEGER);
BEGIN
   POKEV(COL+I,CHR((F-1)*16+B-1));
END;

FUNCTION GCHAR(R,C:INTEGER):INTEGER;
VAR
   S:STRING[1];
BEGIN
   PEEKV(SCR+(R-1)*32+(C-1),S);
   GCHAR:=ASC(S);
END;

PROCEDURE HCHAR(R,C,I:INTEGER);
BEGIN
   POKEV(SCR+(R-1)*32+(C-1),CHR(I));
END;

BEGIN
   GRAPHICS;
   SCREEN(2,14);
   P1[0]:="0000"; P1[1]:="C0C0";
   P1[2]:="3F3F"; P1[3]:="FFFF";
   P2[0]:="000000000000";
   P2[1]:="C0C0C0C0C0C0";
   FOR B:=0 TO 1 DO BEGIN
       FOR A:=0 TO 3 DO BEGIN
           CHAR(32+A+B*4,P1[A]&P2[b]);
       END;
   END;

   FOR A:=0 TO 6 DO BEGIN
       CHAR(40+A*8,"0000000000003F7F");
       CHAR(41+A*8,"010303030303FFFF");
   END;

   COLOR(5,3,4);
   COLOR(6,5,6);
   COLOR(7,7,9);
   COLOR(8,9,10);
   COLOR(9,11,12);
   COLOR(10,13,3);
   COLOR(11,15,16);

   RANDOMIZE;
   WHILE TRUE DO BEGIN
       R:=RND(22)+1;
       C:=RND(29)+1;
       IF GCHAR(R,C)<=39 THEN BEGIN
           IF GCHAR(R,C+1)<=39 THEN BEGIN
               I:=RND(6)*8+40;
               HCHAR(R,C,I); HCHAR(R,C+1,I+1);
               G:=GCHAR(R,C+2);
               IF G<=38 THEN HCHAR(R,C+2,BWOR(G,4));
               G:=GCHAR(R+1,C);
               IF G<=38 THEN HCHAR(R+1,C,BWOR(G,2));
               G:=GCHAR(R+1,C+1);
               IF G<=38 THEN HCHAR(R+1,C+1,BWOR(G,3));
               G:=GCHAR(R+1,C+2);
               IF G<=38 THEN HCHAR(R+1,C+2,BWOR(G,1));
           END;
       END;
   END;
END.

 

And the assembly part:

 

      DEF  POKEV,PEEKV,BREAK,BWOR

WP     BSS  >20
VDPWA  EQU  >8C02
VDPWD  EQU  >8C00
VDPRD  EQU  >8800
VWFLAG BYTE >40
      EVEN

POKEV
      LIMI 0
      AI   R0,-4
      LWPI WP
      MOV  @>8300,R3
      MOV  *R3+,R0
      MOV  *R3,R1
      ANDI R1,>FF
      SWPB R1
      SWPB R0
      MOVB R0,@VDPWA
      SWPB R0
      SOCB @VWFLAG,R0
      MOVB R0,@VDPWA
      MOVB R1,@VDPWD
      LWPI >8300
      LIMI 2
      RT

PEEKV
      LIMI 0
      AI   R0,-4
      LWPI WP
      MOV  @>8300,R3
      MOV  *R3+,R0
      SWPB R0
      MOVB R0,@VDPWA
      SWPB R0
      MOVB R0,@VDPWA
      LI   R1,1
      MOVB @VDPRD,R1
      SWPB R1
      MOV  *R3,R3
      MOV  R1,*R3
      LWPI >8300
      LIMI 2
      RT

BREAK
      LIMI 0
      LWPI WP
      LI   R0,>FF00
      MOVB R0,@0
      LWPI >8300
      LIMI 2
      RT

BWOR
      LIMI 0
      AI   R0,-4
      LWPI WP
      MOV  @>8300,R0
      MOV  *R0+,R1
      MOV  *R0,R0
      SOC  R1,R0
      LWPI >8300
      MOV  @WP,R14
      LIMI 2
      RT

      END

 

BRICKS_TP99.zip

  • Like 2

Share this post


Link to post
Share on other sites

After 2 1/2 years, here's a new version of my favorite "benchmark" program, this time in Java.

Execution speed is just slightly slower than C. You can compare it with my video above.

Code size: 870 bytes for the C version, 1222 bytes for the Java version.

 

Java BRICKS.zip

 

The Java part:

import net.mikekohn.java_grinder.TI99;

public class Bricks {
	static byte[] p1={0x00,0x00,(byte)0xC0,(byte)0xC0,(byte)0x3F,(byte)0x3F,(byte)0xFF,(byte)0xFF};
	static byte[] p2={0x00,0x00,0x00,0x00,0x00,0x00,
		(byte)0xC0,(byte)0xC0,(byte)0xC0,(byte)0xC0,(byte)0xC0,(byte)0xC0};
	static byte[] p3={0x00,0x00,0x00,0x00,0x00,0x00,(byte)0x3F,(byte)0x7F,
		0x01,0x03,0x03,0x03,0x03,0x03,(byte)0xFF,(byte)0xFF};
	static byte[] col={0x10,0x23,0x45,0x68,(byte)0x89,(byte)0xAB,(byte)0xC2,(byte)0xEF};
	
	static public void shadow(int r,int c,int a) {
		byte g=TI99.vdp_read_byte(r*32+c);
		if(g<39) {
			g=(byte)(g|a);
			TI99.vdp_write_byte(r*32+c,g);
		}
	}
	
	static public int random_byte(int c) {
		int SEED=TI99.seed();
		SEED=SEED*0x6FE5+0x7AB9;
		int i=(SEED>>>+(SEED<<;
		TI99.setSeed(SEED);
		return (int)(byte)(i%c);
	}

	static public void main(String args[]) {
		int a,b;
		TI99.vdp_write_reg(7,13);
		
		for(b=0;b<=1;b++) {
			for(a=0;a<=3;a++) {
				TI99.vdp_copy_from_indexed_array(0x900+a*8+b*32,p1,a*2,2);
				TI99.vdp_copy_from_indexed_array(0x902+a*8+b*32,p2,b*6,6);
			}
		}
		for(a=0;a<=6;a++)TI99.vdp_copy_from_sys(0x940+a*64,p3,16);
		TI99.vdp_copy_from_sys(0x384,col,;

		int r,c,i,g;
		while(true) {
			TI99.interrupts();
			r=random_byte(23);
			c=random_byte(30);
			g=TI99.vdp_read_byte(r*32+c);
			
			if(g<40) {
				g=TI99.vdp_read_byte(r*32+c+1);
				if(g<40) {
					i=random_byte(7)*8+40;
					TI99.vdp_write_byte(r*32+c,(byte)i);
					i++;
					TI99.vdp_write_byte(r*32+c+1,(byte)i);
					shadow(r,c+2,4);
					shadow(r+1,c,2);
					shadow(r+1,c+1,3);
					shadow(r+1,c+2,1);
				}
			}
		}
	}
}
The assembly part:

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#include "TI99.h"

TI99::TI99() {}

TI99::~TI99() {
//vdp_copy_from_sys
	fprintf(out, "_vdp_copy_from_sys:\n");
	fprintf(out, "  swpb r0\n");
	fprintf(out, "  movb r0, @VDP_COMMAND\n");
	fprintf(out, "  swpb r0\n");
	fprintf(out, "  ori r0, 0x4000\n");
	fprintf(out, "  movb r0, @VDP_COMMAND\n");
	fprintf(out, "_vdp_copy_from_sys_L1:\n");
	fprintf(out, "  movb *r1+, @VDP_WRITE\n");
	fprintf(out, "  dec r2\n");
	fprintf(out, "  jne _vdp_copy_from_sys_L1\n");
	fprintf(out, "  b *r11\n\n");
}

int TI99::open(const char *filename)
{
  if (TMS9900::open(filename) != 0) { return -1; }

  fprintf(out, "ram_start equ 0xa000\n");
  fprintf(out, "heap_ptr equ ram_start\n");
  
  return 0;
}

int TI99::start_init()
{
  fprintf(out, "\n");
  fprintf(out, ".org 0x6000\n");

  fprintf(out, "  .db 0xaa, 0x01, 0x01, 0x00\n");
  fprintf(out, "  .dw 0x0000\n");
  fprintf(out, "  .dw _prog\n");
  fprintf(out, "  .dw 0x0000\n");
  fprintf(out, "  .dw 0x0000\n");
  fprintf(out, "  .dw 0x0000\n");
  fprintf(out, "  .dw 0x0000\n");
  fprintf(out, "_prog:\n");
  fprintf(out, "  .dw 0x0000\n");
  fprintf(out, "  .dw start\n");
  fprintf(out, "  .db 11, \"BRICKS JAVA\"\n");

  fprintf(out, ".align 16\n\n");

  // Add any set up items (stack, registers, etc).
  fprintf(out, "start:\n");
  fprintf(out, "VDP_COMMAND equ 0x8c02\n");
  fprintf(out, "VDP_WRITE equ 0x8c00\n");
  fprintf(out, "VDP_READ equ 0x8800\n");
  fprintf(out, "  lwpi 0x8300\n");
  
  return 0;
}

int TI99::ti99_vdp_copy_from_sys() {
	fprintf(out, "  mov r%d, r0\n", REG_STACK(reg-3));
	fprintf(out, "  mov r%d, r1\n", REG_STACK(reg-2));
	fprintf(out, "  mov r%d, r2\n", REG_STACK(reg-1));
	fprintf(out, "  mov r11, *r10+\n");
	fprintf(out, "  bl @_vdp_copy_from_sys\n");
	fprintf(out, "  ai r10, -2\n");
	fprintf(out, "  mov *r10, r11\n");
	reg -= 3;
	return 0;
}

int TI99::ti99_vdp_read_byte() {
	fprintf(out, "  swpb r%d\n", REG_STACK(reg-1));
	fprintf(out, "  movb r%d, @VDP_COMMAND\n", REG_STACK(reg-1));
	fprintf(out, "  swpb r%d\n", REG_STACK(reg-1));
	fprintf(out, "  movb r%d, @VDP_COMMAND\n", REG_STACK(reg-1));
	fprintf(out, "  jmp $+2\n");
	fprintf(out, "  clr r%d\n", REG_STACK(reg-1));
	fprintf(out, "  movb @VDP_READ, r%d\n", REG_STACK(reg-1));
	fprintf(out, "  swpb r%d\n", REG_STACK(reg-1));
	return 0;
}

int TI99::ti99_vdp_write_reg() {
	fprintf(out, "  swpb r%d\n", REG_STACK(reg-2));
	fprintf(out, "  swpb r%d\n", REG_STACK(reg-1));
	fprintf(out, "  movb r%d, @VDP_COMMAND\n", REG_STACK(reg-1));
	fprintf(out, "  ori r%d, 0x8000\n", REG_STACK(reg-2));
	fprintf(out, "  movb r%d, @VDP_COMMAND\n", REG_STACK(reg-2));
	reg -=2;
	return 0;
}

int TI99::ti99_interrupts() {
	fprintf(out, "  limi 2\n");
	fprintf(out, "  limi 0\n");
	return 0;
}

int TI99::ti99_vdp_write_byte() {
	fprintf(out, "  swpb r%d\n", REG_STACK(reg-2));
	fprintf(out, "  movb r%d, @VDP_COMMAND\n", REG_STACK(reg-2));
	fprintf(out, "  swpb r%d\n", REG_STACK(reg-2));
	fprintf(out, "  ori r%d, 0x4000\n", REG_STACK(reg-2));
	fprintf(out, "  movb r%d, @VDP_COMMAND\n", REG_STACK(reg-2));
	fprintf(out, "  swpb r%d\n", REG_STACK(reg-1));
	fprintf(out, "  movb r%d, @VDP_WRITE\n", REG_STACK(reg-1));
	reg -= 2;
	return 0;
}

int TI99::ti99_seed() {
	reg += 1;
	fprintf(out, "  mov @0x83C0, r%d\n", REG_STACK(reg-1));
	return 0;
}

int TI99::ti99_setSeed() {
	fprintf(out, "  mov r%d, @0x83C0\n", REG_STACK(reg-1));
	reg -= 1;
	return 0;
}

int TI99::ti99_vdp_copy_from_indexed_array() {
	fprintf(out, "  mov r%d, r0\n", REG_STACK(reg-4));
	fprintf(out, "  mov r%d, r1\n", REG_STACK(reg-3));
	fprintf(out, "  mov r%d, r3\n", REG_STACK(reg-2));
	fprintf(out, "  mov r%d, r2\n", REG_STACK(reg-1));
	fprintf(out, "  a r3, r1\n");
	fprintf(out, "  mov r11, *r10+\n");
	fprintf(out, "  bl @_vdp_copy_from_sys\n");
	fprintf(out, "  ai r10, -2\n");
	fprintf(out, "  mov *r10, r11\n");
	reg -= 4;
	return 0;
}

 

  • Like 3

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...