Jump to content
IGNORED

Developing LUDO game for TI-99/4a in C


xahmol

Recommended Posts

Looked at that existsFile function from Force Command, but see that it is depending on a LOT of other routines such as mds_lvl3_dsrlnk instead of the normal dsklnk (which again uses mds_lvl3_dsrlnkraw......) . Was stubborn and tried the normal dsklnk, but that indeed does not work in detecting if a file is there or not (always returns the same). And want to avoid having to include half of the Force Command DSR code just to do this. That is way overcomplicating things compared to the alternative to just support DSK1 alone.

Suggestion to Matt: your Force Command DSR routines look great. Of course completely your call if you want to spent time on it, but would love to see a file operations C lib based on it, cleaned from Force Command dependencies and that banking magic you are doing there. For now, that is too big a job for me with my very limited knowledge on what actually goes on in how DSR things work on the TI. Would also be strange and hard to maintain if I would do a lib based 100% on your code. But love your code!

 

So for now, have fallen back to support boot tracking for DSK1, DSK2 and DSK3 only and revert to DSK1 if anything else is found. That should cater for most use cases I guess if TIPI users are used to DSK1 mapping anyway.

Doing it more advanced otherwise gets a project on its own (and also would hate to ask for user interaction on the file path).

With that it works now over here in both emulators as my real hardware, also when using call tipi("TIPI.LUDO.LUDO") to start.

 

Will do some more playtesting and than will post a new beta release also here.

Edited by xahmol
  • Like 2
Link to comment
Share on other sites

The direction I am taking Force Command is that the cart is the runtime library. The examples/gcc folder shows what I am playing with..  For example, the FTP client is a ForceCommand executable that loads and calls back into the exposed cartridge API.

 

The plan is to work out what of the internal routines I have should be in the exposed API, test them in that context, and then roll ForceCommand to 2.0. I also want to provide useable SAMS memory management features before 2.0. Probably sometime late this year.

 

My fileExist routine might be suffering quirks of TIPI. TIPI supports some exploits of old hard drive simulating DSK1. files.. If level3 io and target file doesn't exist, it returns 0-skip, allowing traditional DSRLNK to proceed to asking the next device. It is probably a bug that I suspect it also does this for TIPI paths instead of only for mapped drive paths. The DSRLNK in ForceCommand doesn't search across cards. My fileExist routine is probably dependent on that.

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

On 4/6/2021 at 10:01 AM, xahmol said:

Oh, and the other workaround I needed is that cputc() and cputcxy() do not work for non-printable characters (values under 32).

Not sure how it is meant to work in ANSI C, but in CC65 for the targets I coded for that does work for all values between 0 and 255.

Needed that as I do use patterns with codes from 0 to 31, so used a direct vdpchar() from Libti99 instead.

I don't really care what ANSI says to do... but the implementation handles control codes (backspace, form feed, newline, tab) -- are you saying that conio normally does NOT do that?

 

I was pretty sure I used a cc65 reference document to write it, but it's been a while. Also possible I was assuming. ;)

 

 

Link to comment
Share on other sites

13 hours ago, Tursi said:

I don't really care what ANSI says to do... but the implementation handles control codes (backspace, form feed, newline, tab) -- are you saying that conio normally does NOT do that?

 

I was pretty sure I used a cc65 reference document to write it, but it's been a while. Also possible I was assuming. ;)

It does handle control codes, but that was exactly what I was not expecting it to do ;-)

And unsure if it is an incorrect implementation of conio on the Oric Atmos target or not, but it was pretty handy to be able to use cputc to just copy an attribute code to the screen instead of the ASCII control code for the codes under 32.

 

Just did check the CC65 reference documentation, and I actually think you are right. It does not explicitely say so, but as it states there that it supports \n and \r, I am now pretty sure that indeed cputc is supposed to output the control code instead of just copying the value itself to the screen position. 'Like all other conio output functions, cputc distinguishes between \r and \n.'  https://cc65.github.io/doc/funcref.html#cputc

 

Then it is just a damn handy incorrect implementation on the Atmos target of conio.h..... Actually curious now what it then does on the Commodore 64/128 target. On Atmos target, behaviour of cputc is indeed different.... but tend to agree with you now that is actually an incorrect implementation.

Hope now they never correct that Atmos implementation as that would directly break my code, used it extensively to copy color attribute codes to the screen which on the Oric are values under 32 and take a character position instead of going to separate attribute memory...... Funny how every retro computer has its completely different quirks to handle color. You really never can make assumptions coming from another target.

 

Anyway: I then assume that vdpchar() is really the only correct way to print user defined patterns numbered under 32? Not really a problem, just needs vdpchar(gImage+x+32*y,c) instead of just cputcxy(x,y,c). And need of reminding yourself that vdpchar() does not take care of the cursor position so is only a cputcxy replacement and not a cputc.....

Edited by xahmol
Link to comment
Share on other sites

On 4/9/2021 at 4:05 AM, xahmol said:

It does handle control codes, but that was exactly what I was not expecting it to do ;-)

And unsure if it is an incorrect implementation of conio on the Oric Atmos target or not, but it was pretty handy to be able to use cputc to just copy an attribute code to the screen instead of the ASCII control code for the codes under 32.

 

Just did check the CC65 reference documentation, and I actually think you are right. It does not explicitely say so, but as it states there that it supports \n and \r, I am now pretty sure that indeed cputc is supposed to output the control code instead of just copying the value itself to the screen position. 'Like all other conio output functions, cputc distinguishes between \r and \n.'  https://cc65.github.io/doc/funcref.html#cputc

 

Then it is just a damn handy incorrect implementation on the Atmos target of conio.h..... Actually curious now what it then does on the Commodore 64/128 target. On Atmos target, behaviour of cputc is indeed different.... but tend to agree with you now that is actually an incorrect implementation.

Hope now they never correct that Atmos implementation as that would directly break my code, used it extensively to copy color attribute codes to the screen which on the Oric are values under 32 and take a character position instead of going to separate attribute memory...... Funny how every retro computer has its completely different quirks to handle color. You really never can make assumptions coming from another target.

 

Anyway: I then assume that vdpchar() is really the only correct way to print user defined patterns numbered under 32? Not really a problem, just needs vdpchar(gImage+x+32*y,c) instead of just cputcxy(x,y,c). And need of reminding yourself that vdpchar() does not take care of the cursor position so is only a cputcxy replacement and not a cputc.....

I find it a little bit satisfying to read that the decisions I had to make to figure out how to map this stuff onto a Forth system can be just as challenging in C.  Somehow in my imagination I thought C, with more history, would give you function names or some kind of commonly used templates to do this stuff.  It seems from an outsider perspective to be at about the same level as Forth.

 

For what its worth I ended up making a function in assembler vpos(x,y) that computed the screen address and it uses the current gImage  and line-length ( 32,40 or 80 chars) under the hood.

Not sure if that kind of micro-factoring is done in C but it was useful to me as a common factor for some of the other stuff.

Link to comment
Share on other sites

Haha, well, C is old and mature, but C on 8 bits with cross compilers much less so ?

CC65 is relatively mature for Commodore targets. But many other targets are fairly recent. And also C cross compilers on the TI are a fairly recent thing if I read this forum, with libs getting developed basically by the need of somebody who misses them while programming.

So it’s getting there, but you always encounter surprises coming from target to another target. But honestly, that is a large part of the fun doing it ?and coping with playtesting the same game all over again. The fun is solving problems you encounter on a new target, if just copy paste compile for new target would work all the fun would be gone.

  • Like 1
Link to comment
Share on other sites

On 4/9/2021 at 2:05 AM, xahmol said:

It does handle control codes, but that was exactly what I was not expecting it to do ;-)

And unsure if it is an incorrect implementation of conio on the Oric Atmos target or not, but it was pretty handy to be able to use cputc to just copy an attribute code to the screen instead of the ASCII control code for the codes under 32.

 

Just did check the CC65 reference documentation, and I actually think you are right. It does not explicitely say so, but as it states there that it supports \n and \r, I am now pretty sure that indeed cputc is supposed to output the control code instead of just copying the value itself to the screen position. 'Like all other conio output functions, cputc distinguishes between \r and \n.'  https://cc65.github.io/doc/funcref.html#cputc

 

Then it is just a damn handy incorrect implementation on the Atmos target of conio.h..... Actually curious now what it then does on the Commodore 64/128 target. On Atmos target, behaviour of cputc is indeed different.... but tend to agree with you now that is actually an incorrect implementation.

Hope now they never correct that Atmos implementation as that would directly break my code, used it extensively to copy color attribute codes to the screen which on the Oric are values under 32 and take a character position instead of going to separate attribute memory...... Funny how every retro computer has its completely different quirks to handle color. You really never can make assumptions coming from another target.

 

Anyway: I then assume that vdpchar() is really the only correct way to print user defined patterns numbered under 32? Not really a problem, just needs vdpchar(gImage+x+32*y,c) instead of just cputcxy(x,y,c). And need of reminding yourself that vdpchar() does not take care of the cursor position so is only a cputcxy replacement and not a cputc.....

Ah well. Conio is the slowest output method in libti99 and was only implemented for someone who was porting something. ;)

 

There's also a macro you can drop into vdpchar - VDP_SCREEN_POS(row,col)

For continuing output, the TI VDP has its own address register, so once anything has set the screen position, assuming you don't change it, you can just write bytes directly to VDPWD and they will continue on across the screen. ( VDPWD='A'; ) This is much faster than reloading the address for every character (3 times as fast, in fact). String writes take advantage of this.

 

Of course, that's all getting very TI specific.

 

  • Like 4
Link to comment
Share on other sites

Thanks for the explanation Tursi, will probably use that if I ever want to make something TI specific.

For this board game, anything faster than BASIC is quickly quick enough ? so there I prefer to keep code cross platform enabled to the largest extent possible.

But I might actually use VDPWD for the drawing of the window borders as I could not use cputc() there anyway (border graphics are in those below 32 patterns). Also that is about the only place where I think an increase in speed would actually be noticeable to the user in my program.

 

And just to check if I understand this correctly: how would I set the start x,y position for VDPWD to start with?

I see it defined in vdp.h as #define VDPWD    *((volatile unsigned char*)0x8C00)

So would pointing it at a different x,y position just be putting gImage+VDP_SCREEN_POS(r,c) somewhere?

 

Edit: never mind, already found I assume: VDP_SET_ADDRESS_WRITE(pAddr)

At least that is what the vdp_char.c source does:

#include "vdp.h"


void vdpchar_default(int pAddr, int ch) {
	VDP_SET_ADDRESS_WRITE(pAddr);
	VDPWD=ch;
}

void (*vdpchar)(int pAddr, int ch) = vdpchar_default;

 

Edited by xahmol
  • Like 1
Link to comment
Share on other sites

Works. Updated amongst other my window border draw, pull down menu and clear area routines (so basically all routines that had for loops in them using vdpchar() before).

Noted also that VDP_SET_ADDRESS_WRITE(pAddr); and VDPWD=ch; do not update the cursor for conio.h and vice versa, so if you mix this with conio output functions you have to set cursor positions seperately.

 

Have indeed the feeling that the updated routines are now indeed noticably quicker, so thanks.

 

Uploaded a new version as ZIP in the start post.

 

Edited by xahmol
Link to comment
Share on other sites

19 hours ago, xahmol said:

But I might actually use VDPWD for the drawing of the window borders as I could not use cputc() there anyway (border graphics are in those below 32 patterns). Also that is about the only place where I think an increase in speed would actually be noticeable to the user in my program.

 

Looks like you found it - you could also consider just calling hchar (horizontal character with repeat) and vchar (vertical character with repeat).. these are straight out of TI BASIC, thus the names, but are optimized. :)

 

On the other hand, you already have it working, so no need to change it. ;)

 

And that is true on the conio position. VDP_SET_ADDRESS_WRITE and VDPWD are directly writing to the hardware, which is not what conio uses. One of the reasons not to use the hardware address is that it's impossible to read it back. :)

 

  • Thanks 1
Link to comment
Share on other sites

hchar and vchar would indeed make even more sense, especially because I now also have made a print x spaces function ?

OK, one more optimisation to do.

 

Did not use the conio chlinexy and cvlinexy on the original as in the CC65 functions you do not get to choose the character to repeat, it just chooses the system default. Which is plain ugly, especially on targets that do not have lines in it's standard charset.

 

(that is the problem with porting things. You start to look for literal replacements instead of rewriting optimised to native instructions.... Especially as the original source code is Commodore 128 BASIC7.0, ported via Oric Atmos BASIC to CC65 C code for Atmos target)

 

EDIT: Optimisation with hchar and vchar done. New ZIP in startpost.

Edited by xahmol
  • Like 1
Link to comment
Share on other sites

So now have my menumakeborder() function like this, now completely without need for any for loop:

void menumakeborder(unsigned char xpos, unsigned char ypos, unsigned char height, unsigned char width)
{
    /* Function to make menu border
       Input:
       - xpos: x-coordinate of left upper corner
       - ypos: y-coordinate of right upper corner
       - height: number of rows in window
       - width: window width in characters        */
 
    windowsave(ypos, height+2);

    cleararea(xpos+1,ypos+1,height,width);
    VDP_SET_ADDRESS_WRITE(gImage+xpos+(ypos*32));
    VDPWD = C_LOWRIGHT;
    hchar(ypos,xpos+1,C_LOWLINE,width);
    VDPWD = C_LOWLEFT;
    vchar(ypos+1,xpos,C_RIGHTLINE,height);
    vchar(ypos+1,xpos+width+1,C_LEFTLINE,height);
    VDP_SET_ADDRESS_WRITE(gImage+xpos+(ypos+height+1)*32);
    VDPWD = C_UPRIGHT;
    hchar(ypos+height+1,xpos+1,C_UPLINE,width);
    VDPWD = C_UPLEFT;
}

By the way: funny that TI uses row, column (so first the y, then the x) instead of x,y on basically every other target and conio.h..... Caused me of course to mix up the co-ordinates the first time trying hchar and vchar. But see you faithfully implemented the TI BASIC original which I see from the manual also uses r,c.

Edited by xahmol
Link to comment
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.
Note: Your post will require moderator approval before it will be visible.

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...