Sunday 26 July 2015

Invaders with Processing


Something I did on a boring Sunday afternoon. It's amazing how quickly these things can be done nowadays, as the programming can be extremely lazy. (Cough) I followed a "if it works don't look back" style of programming so obviously it could be a lot shorter and more efficient. I wanted to relax anyway.

I have no idea if it will work smoothly on different computers. The video shows roughly how it plays on my Linux Mint/Mate with 3GHz and a Radeon EAH5750 display adapter. Different Java implementations may result in different sync.

Install Processing, Copy/paste the following onto the sketch and off you go. The arrow keys control the ship. Up arrow is fire. Q resets the game anytime.

// Invaders game by Tero Heikkinen
// left/right arrows - Move
// up arrow - Fire
// q - restart game
// Made in one evening 26.7.2015

int MAXOBJECTS=100;
int []g_x=new int [MAXOBJECTS];
int []g_y=new int [MAXOBJECTS];
int []g_t=new int [MAXOBJECTS];
int []g_vx=new int [MAXOBJECTS];
int []g_vy=new int [MAXOBJECTS];
int []g_score=new int[10];
int g_pulse,g_level,g_aframe;
boolean g_joyr,g_joyl,g_joyf;

void setup()
{
  size(600,400);noSmooth();noStroke();fill(255,255,255);
  resetgame();
}

void create_wave()
{
  int diffi,xon,flip;
  float floi;
  g_level++;
  diffi=g_level;flip=0;
  if(diffi>10){diffi=10;}
  for(int j=0;j<=3;j++){
    for(int i=0;i<=7;i++){
      floi=0;
      if(g_level==4||g_level==8||g_level==16){
        floi=float(i);
      }
      xon=64*i;
      if(g_level==6||g_level==10||g_level==14||g_level==20){xon=64*i+flip*32;}
      addobject(xon,32+diffi*16+26*j+int(cos(floi)*16),1,0,2);  
      }
    flip++;if(flip>1){flip=0;}
  }
  int mzip=-2;
  if(random(100)<50){mzip=2;}
  //add motherships
  addobject(width*2,32,mzip,0,25); 
  addobject(width*4,32,mzip,0,25);
}

void resetgame()
{
  for(int i=0;i<=9;i++){g_score[i]=0;}
  clearobjects();g_level=0;
  addobject(width/2-16,height-32,0,0,1);
}

void graphic(int x,int y,String gfx)
{
  int xc,yc,xp,yp;
  xc=0;yc=0;
  for (int i=0;i<gfx.length();i++){
  if(gfx.charAt(i)=='1'){
    xp=x+xc*4;yp=y+yc*2;
    if(xp>=0&&xp<=width-4&&yp>=0&&yp<=height-2){
      pixels[xp+(yp)*width]=0xffffffff;
      pixels[xp+(yp)*width+1]=0xffffffff;
      pixels[xp+(yp)*width+2]=0xffffffff;
      pixels[xp+(yp)*width+3]=0xffffffff;
    }
  //rect(x+xc*4,y+yc*2,4,2);
  }
  xc++;
  if(gfx.charAt(i)==' '){xc=0;yc++;}
  }
}

void addobject(int x,int y,int vx,int vy,int t)
{
  int first;
  first=0;
  if(t!=1){first=1;}
  
  //alien bullet check fit
  if(t==4){ 
      for(int i=0;i<MAXOBJECTS;i++){
        if(g_t[i]==2){
          if(x>g_x[i]&&x<g_x[i]+24&&y>g_y[i]-4&&y<g_y[i]+16){
          return;
        }
      }
    }
  }
  
  // add an object
  for(int i=first;i<MAXOBJECTS;i++){
    if(g_t[i]==0){
      g_vx[i]=vx;g_vy[i]=vy;
      g_x[i]=x;g_y[i]=y;g_t[i]=t;return;
    }
  }
}

void clearobjects()
{
  for(int i=0;i<MAXOBJECTS;i++){
    g_t[i]=0;g_vx[i]=0;g_vy[i]=0;g_x[i]=0;g_y[i]=0;
  }
}

void command_fleet(int com)
{
  for(int i=0;i<MAXOBJECTS;i++){
    if(g_t[i]==2){
      if(com<5){
        g_vx[i]=com;
        g_x[i]=g_x[i]+com;
      }
      if(com==16&&g_t[0]==1){
        g_y[i]=g_y[i]+12;
      }
    }
  }
}

void addscore(int ss)
{
  int okay;
  okay=0;
  g_score[0]=g_score[0]+ss;
  while(okay==0){
    okay=1;
    for(int i=0;i<=9;i++){
      if(g_score[i]>9){g_score[i+1]++;g_score[i]=g_score[i]-10;okay=0;}
    }
  }
}

void drawobjects()
{
  int aliens,multip,order;
  loadPixels();
  aliens=0;multip=1;order=0;
  for(int i=0;i<MAXOBJECTS;i++){
    if(g_t[i]==2){aliens++;}
  }
  if(aliens==0){create_wave();}
  if(aliens<=9){multip=2;}
  if(aliens<=3){multip=3;}
  if(aliens==1){multip=4;}
  g_aframe=g_aframe+multip;
  if(g_aframe>31){g_aframe=g_aframe-32;}
  
  for(int i=0;i<MAXOBJECTS;i++){
    switch(g_t[i]){
    case 1:
      graphic(g_x[i],g_y[i],"00011000 00011000 01111110 00111100 11111111 11111111");
    break;
    case 2:
    if(g_aframe<=15){
      graphic(g_x[i],g_y[i],"00000000 11011011 00111100 11011011 11111111 00100100 01100110");
    }
    else
    {
      graphic(g_x[i],g_y[i],"10000001 01011010 00111100 11011011 11111111 01000010 11000011");
    }
       g_x[i]=g_x[i]+g_vx[i]*multip;
       g_y[i]=g_y[i]+g_vy[i]*multip; 
       if(g_y[i]>g_y[0]-16&&g_t[0]==1){g_t[0]=8;}
       if(g_x[i]>width-32){order=1;}
       if(g_x[i]<=0){order=2;}
      aliens++;
      int frate;
      frate=g_level*multip;
      if(frate>200){frate=200;}
      if(random(400)<frate){
        int zzoo;
        zzoo=2;
        if(g_level>=4){zzoo=4;}
        if(g_level>=8){zzoo=6;}
        addobject(g_x[i]+12,g_y[i]+24,0,zzoo,4);
      }
    break;
    case 3:
      graphic(g_x[i],g_y[i],"11 11");
      if(g_y[i]<0){g_t[i]=0;}
      for(int j=0;j<MAXOBJECTS;j++){
        if((g_t[j]==2||g_t[j]==25)&&g_x[i]>g_x[j]-8&&g_x[i]<g_x[j]+32&&g_y[i]>g_y[j]&&g_y[i]<g_y[j]+16){addscore(1*g_t[j]);g_t[j]=8;g_t[i]=0;}
      }
      g_x[i]=g_x[i]+g_vx[i];
      g_y[i]=g_y[i]+g_vy[i]; 
    break;
    case 4:
      graphic(g_x[i],g_y[i],"11 11 11");
      if(g_y[i]>height){g_t[i]=0;}
      int j=0;
      if(g_t[j]==1&&g_x[i]>g_x[j]-6&&g_x[i]<g_x[j]+30&&g_y[i]>g_y[j]&&g_y[i]<g_y[j]+16){g_t[j]=8;g_t[i]=0;}   
      g_x[i]=g_x[i]+g_vx[i];
      g_y[i]=g_y[i]+g_vy[i]; 
    break;
    case 8:
       graphic(g_x[i],g_y[i],"10010011 01010110 00111100 11000011 0111100 01101010 11001001");
       g_vy[i]++;if(g_vy[i]>3){g_vy[i]=0;g_t[i]=9;}
    break;
    case 9:
       graphic(g_x[i],g_y[i],"00000000 01010010 00000000 10000001 0000000 01001010 0000000");
       g_vy[i]++;if(g_vy[i]>3){g_vy[i]=0;g_t[i]=0;}
    break;
    case 25: //mothership
       graphic(g_x[i],g_y[i],"00111110 01010101 01111111 00100010 00011100");
       g_x[i]=g_x[i]+g_vx[i];
       g_y[i]=g_y[i]+g_vy[i]; 
       if(g_x[i]<-32){g_x[i]=width*6;}
       if(g_x[i]>width*6){g_x[i]=-32;}
    break;
    }
  }
  if(order==1){command_fleet(16);command_fleet(-1);}
  if(order==2){command_fleet(16);command_fleet(1);}
  if(g_t[0]==0){
    graphic(32,16,"00111100 01000010 01000010 01010010 01001010 00111100");
  }
  scoreboard();
  updatePixels();
}

void scoreboard()
{
  int s,x,y;
  x=96+4*32;y=16;
  s=1;
  for(int i=0;i<=4;i++){
  switch(g_score[i]){
    case 0:graphic(x,y,"00111100 01000010 01000010 01000010 01000010 00111100");break;
    case 1:graphic(x,y,"00001000 00011000 00101000 00001000 00001000 00111110");break;
    case 2:graphic(x,y,"00111100 01000010 00000010 00111110 01000000 01111110");break;
    case 3:graphic(x,y,"01111110 00000010 00011100 00000010 01000010 00111100");break;
    case 4:graphic(x,y,"00011100 00100100 01000100 01111110 00000100 00000100");break;
    case 5:graphic(x,y,"01111110 01000000 01111100 00000010 01000010 00111100");break;
    case 6:graphic(x,y,"00111100 01000000 01111110 01000010 01000010 00111100");break;
    case 7:graphic(x,y,"01111110 00000010 00011110 00000010 00000100 00001000");break;
    case 8:graphic(x,y,"00111100 01000010 00111100 01000010 01000010 00111100");break;
    case 9:graphic(x,y,"00111100 01000010 01111110 01000010 00000010 00111100");break;
    }
    x=x-32;
  }
}

void keyPressed()
{
  if(key=='q'){resetgame();}
  if(key==CODED){
    if(keyCode==RIGHT)g_joyr=true;
    if(keyCode==LEFT)g_joyl=true;
    if(keyCode==UP)g_joyf=true;
  }
}

void keyReleased()
{
  if(key==CODED){
    if(keyCode==RIGHT){g_joyr=false;}
    if(keyCode==LEFT){g_joyl=false;}
    if(keyCode==UP){g_joyf=false;}
  }
}

void draw()
{
  background(0,0,0);
  drawobjects();
  if(g_joyr){if(g_x[0]<width-36){g_x[0]=g_x[0]+4;}}
  if(g_joyl){if(g_x[0]>4){g_x[0]=g_x[0]-4;}}
  if(g_joyf&&g_pulse==0){g_pulse=10;if(g_t[0]==1){addobject(g_x[0]+12,g_y[0],0,-8,3);}}
  if(g_pulse>0){g_pulse--;}
}


Thursday 23 July 2015

The Quest for the MSX cross-compilation


My innocent hope was to get MSX cross compiling c/inline z80 assembler working on my Linux.
I thought it would be fairly simple as I already had it running on the Macintosh, and the Linux is supposed to be the coder's friend.

How wrong I was. Well, I didn't destroy anything in a fit of rage, nor did I have to sleep over this, so in a sense this was pretty successful. Also, what I've done here is comparatively simple to actually having done the groundwork, I'm simply trying to get someone else's solutions to work.

Admittedly, some woes came out of my broken Makefile which I had fiddled to get it work on the Mac, making it less general-purpose in the process.

This is not a how-to, just a recollection of how many annoying stages I had to go through.

The evening went something like this:

-Install sdcc from the repositiories. Find out the inline assembler does not compile.
-Get various z80 compilers and fiddle around with the makefile
-Find out that the sdcc version is too new to handle inline z80 in this way
-Remove sdcc
-Get old sdcc sources which won't compile.
-Mess around trying to configure and build the sources, installing stuff like bison in the process.
-Find out the version is too old or inappropriate to build anyways or something.
-Remove sdcc
-Find out there's a ready made version 2.9 that works.
-Download and copy the stuff to /usr/local/bin
-The MSX stuff starts to compile but needs hex2bin
-Get hex2bin sources
-Compile hex2bin
-Copy the stuff to /usr/local/bin
-Now I get the .com out of it but the .dsk outputting does not work. It requires a small thing called wrdisk.
-Find and get wrdisk
-Compile wrdisk
-Copy the stuff to /usr/local/bin
-Install openmsx from the repository
-Find out it does not load anything from disk without a proper MSX system rom.
-Copy the system rom from Macintosh.
-Spend quarter of an hour trying to figure out how openmsx accepts the system rom. From the unnecessarily complex manuals I find it copies into ~/.openmsx/systemroms, but nobody says how to invoke it
-Make several guesses as to what to pass onto the -machine parameter of openmsx.
-Guess correctly that openmsx -machine Toshiba_HX-10 works, even though that is not the filename.
-Find out that as the Toshiba HX-10 does not have a f*cking disk rom, it won't load the .dsk image.
-Copy the MSX2 FS-A1WSX roms but find out the openmsx won't run it as the files are lacking.
-Find out I've actually used a FS-A1WX rom variant, copy them instead.
-openmsx -machine Panasonic_FS-A1WX finally runs an MSX with a disk rom.
-Triumphantly run openmsx -machine Panasonic_FS-A1WX jaa.dsk to run the compiled file...
...to find out the autoexec expects a different name.
-Fiddle some more with the Makefile to get the proper autoexec from the project folder.
-Wait an eternity for the openmsx to boot up and load the command.com and the autoexec and execute the damn file. Curse the entire platform.
-Profit!

I probably can't even remember all the phases. Add to that the constant figuring out of proper terminal syntax and the appropriate folders. Now I'm spent and can't bother to even make the small piece of code adjustment I was supposed to do...

Wow, it was really worth it...

Thursday 16 July 2015

Return of the Schneider Euro PC



I've bought another Schneider Euro PC. This one has the power supply included+Schneider Joystick and mouse. The computer has a game card installed, which is pretty useless though: it simply has two joystick ports.

The outer appearance was very promising: virtually no yellowing on the computer or the keyboard.

Compare this to my other Euro PC, which I discussed here. In this picture, it's the one below.

The case yellowing is not as visible from the photo, but it's there.

However, a look at the inside revealed a sad truth: the battery has died and corroded some parts of the PCB.

Does not look too bad? Look at the surface mounted chip peeping at the bottom right.
Nurture, not nature. My other Euro PC computer had yellowed heavily, but the board had remained intact. The reverse has happened here, the board has suffered but not the cover.

I of course removed the battery. Also, did some reading on the net, and it appears that the PCB corrosion is a many-splendored thing. It would be best to take a board with this damage to a professional...

Well, I simply lathered the PCB with WD40 (Good enough for your dad, good enough for you) and started cleaning it with Q-tips and scratching between pins with a paper knife. Edit: I've since learned you're NOT supposed to use WD40 for cleaning PCBs.

I'd like to say this heroic activity had some positive outcome. But... no, on power-up the machine just lets out one horrendous wailing beep, and that's it.


Connecting to Commodore 1084S

It's not all sadness and gloom, though. At least I got a proper power supply for my older Schneider Euro PC, which has been proven to work. Also, as I now have a monitor with TTL RGB support I could easily connect my Euro PC to it. I could have arranged both by other means, but it would have been more trouble. 

Now what to do? The Euro PC has the only working PC floppy drive in the house.
I still needed the cable, though. I went to a flea market and bought a fat old SCART cable. My soldering has gotten (more) rusty, so building the cable took a surprisingly long time. Hint: It's worth removing all the unneeded wires near the end so you can fit it into the DIN housing...  It's also a great idea to shove the DIN rubber housing on the cable before soldering the connector.

My monitor has the DIN variant of the TTL RGB, other monitors may have the 9-pin D SUB. Note the absence of SCART type connector.

In the end, contrary to all past experiences, my cable worked on the first go.

The information is not too hard to find out but here are the pinouts anyway:

Euro PC monitor pinout at the back of the computer:
1 Ground
2 Ground
3 Red
4 Green
5 Blue
6 Intensity
7 Monovideo
8 Horizontal Sync
9 Vertical Sync

Commodore 1084S TTL RGB (DIN 45326) at the back of the monitor:

1 Status comp
2 Red
3 Green
4 Blue
5 Intensity
6 Ground
7 Horizontal Sync /CS
8 Vertical Sync

I connected everything directly to their corresponding pins. I connected the two grounds from the Euro PC to the one ground pin of the monitor. The "status comp" I connected to the "Monovideo".