Porting PLATOTERM to the QL as a QDOS app.

Anything QL Software or Programming Related.
Post Reply
tschak909
ROM Dongle
Posts: 14
Joined: Sat Jul 06, 2019 5:00 am

Porting PLATOTERM to the QL as a QDOS app.

Post by tschak909 »

Hello everyone.

I've spent the last two years building a massive on-line platform for retrocomputing users (IRATA.ONLINE), based on PLATO, and to this end have written almost TWO DOZEN ports of the required terminal access software PLATOTERM.

It can be gotten here, for many platforms, including the ZX Spectrum: https://www.irata.online/

Now I am trying to get up to speed as quickly as possible to make a full screen QDOS application (there are various reasons for this, if you really want to know, ask and I'll explain.)

and I have put together a toolchain based on XTC68 and the C68 libs/includes.

Thus far, I have been able to compile, link, and test simple programs in Qemulator, and am now trying to set up the correct drawing context by opening my own channel and not dealing with the default CON that C68 seems to create.

I did try to redirect the consetup function pointer, to a blank function prototype (to test and see if I was correctly casting the pointer), but the default CON_ still appears.
https://github.com/tschak909/platotermq ... main.c#L22
https://github.com/tschak909/platotermq ... reen.c#L28

What's going on here? also, why is my program task name still showing as C_PROG (when I look at it via file), despite me changing it?

Assuming I can get past this, is getting the window I want, simply a matter of calling io_open against the appropriate SCR or CON specification? (I am leaning towards SCR, as I do not want normal keyboard input semantics, and want to handle this on my own.)

also, I have noticed the sd_iscale function, why only the single scale value? Can I only symmetrically scale both X and Y? (I can work around this, just like I do everywhere else by using scaling tables, but I wanted to see if I could leverage the system ROM to give me a 512x512 display scaled down to 512x256 or 256x256 depending on the display mode)

-Thom


User avatar
NormanDunbar
Forum Moderator
Posts: 2251
Joined: Tue Dec 14, 2010 9:04 am
Location: Leeds, West Yorkshire, UK
Contact:

Re: Porting PLATOTERM to the QL as a QDOS app.

Post by NormanDunbar »

For the job name:

Code: Select all

char _prog_name[] = "program_name";
As a global assignment, outside of all functions, including main(). I don't know about there being a channel open but I suspect it's for stdin,stdout,stderr maybe.

I suspect, perhaps that this might remove the default channel:

Code: Select all

char _conname[] = NULL;
But I haven't tested it.


EDITED: To add details about _conname.

Cheers,
Norm.


Why do they put lightning conductors on churches?
Author of Arduino Software Internals
Author of Arduino Interrupts

No longer on Twitter, find me on https://mastodon.scot/@NormanDunbar.
tschak909
ROM Dongle
Posts: 14
Joined: Sat Jul 06, 2019 5:00 am

Re: Porting PLATOTERM to the QL as a QDOS app.

Post by tschak909 »

The software is now almost functional, it is starting to render correctly, and is starting to read from keyboard and i/o to the serial port, I recorded a demonstration of what's happening:
sinclairql-problems.gif
The incoming buffer isn't draining properly, for some reason... What's the correct pattern for dealing with I/O ?

I am doing the following in main.c:

Code: Select all

  for (;;)
    {
      io_main();
      keyboard_main();
      touch_main();
    }
io_main:

Code: Select all

/**
 * io_main() - The IO main loop
 */
void io_main(void)
{
  char ch;
  padByte pb;
  if (io_pend(ser,0)==0)
    {
      io_fbyte(ser,-1,&ch);
      pb=ch;
      ShowPLATO(&pb,1);
    }
}
keyboard_main()

Code: Select all

/**
 * keyboard_main - Handle the keyboard presses
 */
void keyboard_main(void)
{
  char ch;
  if (io_pend(win,0)==0)
    {
      io_fbyte(win,0,&ch);
      if (ch==0x0a)
        ch=0x0d;
      io_send_byte(ch);
    }
}
touch_main is currently empty.

What could be going on?

-Thom


User avatar
tofro
Font of All Knowledge
Posts: 2685
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: Porting PLATOTERM to the QL as a QDOS app.

Post by tofro »

What does ShowPLATO() do? How much time does it take? If that's too much (you seem to be drawing fairly complex stuff there), you will easily lose characters. In that case, it would make sense to split off serial receive in its own job and feed a pipe to ShowPLATO() in this job.

Serial comms on the QL is somewhat shaky: There are no hardware buffers, and characters the 8049 cannot deliver to the main CPU immediately are silently discarded.

Do you use any type of handshake? If yes, is your cable wired properly?

What baud rate are you receiving at?

Also take note that

Code: Select all

if (io_pend(...) {
   io_fbyte (exactly one character)
}
Is potentially dangerous: If there's more than one character arriving between calls, you'll never read everything available and inevitably lose characters. This should be more like

Code: Select all

while (io_pend(...)
   io_fbyte (one character)
Tobias


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
tschak909
ROM Dongle
Posts: 14
Joined: Sat Jul 06, 2019 5:00 am

Re: Porting PLATOTERM to the QL as a QDOS app.

Post by tschak909 »

ShowPLATO() is the primary state machine, in protocol.c
https://github.com/tschak909/platotermq ... col.c#L781

Data goes in, and is interpreted, and various callbacks are called to handle screen drawing, etc. It is very complex, yes, but it needs to be.

I did try to do an aggressive buffering technique where I keep grabbing data as long as io_pending was returning 0, but I was still getting the same behavior.

Nominally, I am testing at 1200 baud in qemulator, at "original QL speed"

SER1 is set up to use COM1, which is attached to tcpser on the other side, set up for hardware handshaking.

I am not losing characters, I am having to press keys on the keyboard to get the data to come across, and I am not sure why.

I have written over TWO DOZEN versions of this for other platforms, all from the same core C code, and would really like to get over this hump, so I can get you guys an excellent terminal emulator to access IRATA.ONLINE (and other PLATO systems)

-Thom


User avatar
tofro
Font of All Knowledge
Posts: 2685
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: Porting PLATOTERM to the QL as a QDOS app.

Post by tofro »

What you could try for the moment is, change your receiving code (both console and ser) to

Code: Select all

if (io_fbyte (ch, 0, &c) == 0) {
   // got something
} else
   // got nothing
This saves the io_pend calls (trying to read with zero timeout returns with ERR_NC if nothing there), so the code is effectively the same, but saving some traps.

Actually, I don't see a good reason why the program should be waiting for keypresses.

Tobias


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
tschak909
ROM Dongle
Posts: 14
Joined: Sat Jul 06, 2019 5:00 am

Re: Porting PLATOTERM to the QL as a QDOS app.

Post by tschak909 »

How can I set up a seperate job for the serial i/o? I see mt_cjob()...

-Thom


tschak909
ROM Dongle
Posts: 14
Joined: Sat Jul 06, 2019 5:00 am

Re: Porting PLATOTERM to the QL as a QDOS app.

Post by tschak909 »

And by removing the io_pend() calls for keyboard and serial i/o, and depending on a zero time-out to simply return a NC, I am able to get the buffer to behave as expected, and am now able to successfully connect into IRATA.ONLINE, without any hiccups, at 9600 baud in Norma QL speed (using RTS/CTS), as to how this fares to a real QL, we'll have to see, would love to buy or borrow a unit for dev testing.
ql-successful.gif
The new bits of code:

Code: Select all

/**
 * keyboard_main - Handle the keyboard presses
 */
void keyboard_main(void)
{
  char ch;
  int ret;
  ret=io_fbyte(win,0,&ch);
  if (ret==0)
    {
      if (ch==0x0a)
        ch=0x0d;
      io_send_byte(ch);
    }
}

Code: Select all

/**
 * io_main() - The IO main loop
 */
void io_main(void)
{
  char ch;

  while (io_fbyte(ser,0,&ch)==0)
    {
      buff[len++]=ch;
    }

  ShowPLATO(buff,len);
  len=0;
}
Now onto getting the text output fixed, making sure it doesn't wrap or break lines, then implementing the rest of the missing draw modes, implementing the new fonts, implementing font downloading, and finally implementing preferences.


User avatar
tofro
Font of All Knowledge
Posts: 2685
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: Porting PLATOTERM to the QL as a QDOS app.

Post by tofro »

tschak909 wrote:How can I set up a seperate job for the serial i/o? I see mt_cjob()...

-Thom
That's what I use (It's assembler, but just use it, it was written By TT, the creator of QDOS. Ready to be compiled by C68-as). A data space of 500 bytes works well for me for most functions. Hand the independent job the end of an opened pipe to your main program or some shared buffer area it can place received characters in. You might want to set the job's priority higher than in your main program, and your loop will be able to block in serial input.:

Stolen from Tony Tebby's C PTR example routines:

Code: Select all

;******************************************************************************
;
; The function jcall sets up a C function as a dependant job.
; This Job can share the workspace of its parent.
;
; It returns the JOBID or 0 if the job fails. In this case, _oserr is set.
;
; The call parameters are:
;                            char *     function
;                            char *     job name
;                            long       priority
;                            long       data space
;                            long       number of parameters to pass
;                            .....      parameters passed to function.
;
;******************************************************************************

.globl _jcall

sms.myjb equ    -1
sms.crjb equ    $01
sms.frjb equ    $05
sms.acjb equ    $0a

jcb_a0   equ    $40
jcb_a7   equ    $5c
jcb_end  equ    $68

_jcall:

movem.l d2-d4/d7/a2/a3,-(sp)
stk_func equ    $1c
stk_jnam equ    $20
stk_prio equ    $24      
stk_data equ    $28
stk_npar equ    $2c      
stk_parm equ    $30

        move.l  stk_jnam(a7),a0          ; first we count that characters
        moveq   #-1,d4                   ; of the jobname 'cos C does not
jc_cntc:                                 ; know about strings
        addq.l  #1,d4
        tst.b   (a0)+
        bne     jc_cntc

        move.w  sr,d7
        move.l  a7,a3
        trap    #0                       ; atomic while we patch the jcb

        moveq   #sms.myjb,d1             ; create for me
        moveq   #11,d2                   ; header and round up
        add.l   d4,d2
        and.w   #$fffe,d2                ; asm bug in bclr
        move.l  stk_data(a3),d3          ; data space
        sub.l   a1,a1

        moveq   #sms.crjb,d0
        trap    #1
        move.l  d0,__oserr                ; error?
        blt     jc_njobu                 ; ... yes

        move.l  stk_func(a3),jcb_a0-jcb_end(a0) ; set Job's A0

        move.l  stk_npar(a3),d0
        lsl.l   #2,d0                    ; 4 bytes per parameter
        lea     stk_parm(a3,d0.l),a1     ; the parameters
        move.l  jcb_a7-jcb_end(a0),a2    ; and push bits onto the stack
        bra     jc_parme
jc_parml:
        move.l  -(a1),-(a2)              ; push parameter
jc_parme:
        subq.l  #4,d0
        bge     jc_parml

        move.l  a2,jcb_a7-jcb_end(a0)    ; set job's stack pointer

        lea     jc_header,a1

        move.l  (a1)+,(a0)+              ; header
        move.l  (a1)+,(a0)+
        move.w  d4,(a0)+                 ; job name

        move.l  stk_jnam(a3),a1
jc_cname:
        move.b  (a1)+,(a0)+
        bne     jc_cname

        move.w  d7,sr                    ; back to user mode

        moveq   #sms.acjb,d0             ; activate
        move.l  stk_prio(a7),d2
        moveq   #0,d3                    ; and do not wait
        trap    #1
        move.l  d0,__oserr
        bne     jc_njob

        move.l  d1,d0                    ; job id
jc_exit:
        movem.l (sp)+,d2-d4/d7/a2/a3
        rts
jc_njobu:
        move.w  d7,sr                    ; back to user mode
jc_njob:
        moveq   #0,d0
        bra     jc_exit


jc_header: jmp  jc_wrapper                ; jump to wrapper
        dc.w   $4afb


jc_wrapper:
        jsr     (a0)                     ; jsr to function
jc_die:
        moveq   #sms.frjb,d0
        moveq   #sms.myjb,d1
        moveq   #0,d3
        trap    #1                       ; then die

This assembler routine can run a C function as independent job. It is linked to the executable and called like

Code: Select all

mjobid=jcall ((char *) function_to_run,"Name of function to run", job_priority, job_dataspace_size, num_params, params, ...));
PS: I have actually no idea why the removal of the IO.PEND calls seems to have solved your problem. The only explanation I have is that some device driver (maybe an emulation issue) didn't like being hammered at so hard ;)


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
tschak909
ROM Dongle
Posts: 14
Joined: Sat Jul 06, 2019 5:00 am

Re: Porting PLATOTERM to the QL as a QDOS app.

Post by tschak909 »

call to sd_fount() doesn't seem to make a difference, am defining the font here: https://github.com/tschak909/platotermq ... src/font.h

and calling it here:
https://github.com/tschak909/platotermq ... een.c#L169

Not getting an error code back, but it's also not changing the font. ARRGHH...

-Thom


Post Reply