I have access to ones at school that I could test his code on my 86 with, though the PC link they use is rather buggy so saving or uploading logs would be a bit difficult.
I don't have an oscilloscope. I sure wish I did!

I could try using LINE IN on my soundcard as a poor man's oscilloscope. At a 48KHz (or 96KHz if my soundcard supports it) sample rate I could at least see the overall frame timings and where the bytes start and end, and possibly make out some of the bits too. The bit-level protocol is cutting it close at 48KHz.

I'll see what I can do tomorrow. I need to get some sleep tonight. Smile
Yeah, like Jonimus, saving and uploading data from the oscilloscope is a problem with the oscilloscopes I have access to at work, AFAICT.
Christop, if you do, make sure you put some isolation circuitry first so you don't destroy your sound card. Smile
Pah, isolation circuitry! The linkport is only ~5 volts, and it's not grounded at all so it's already "floating" (I wouldn't plug it into my soundcard if I'm using a 5V wallwort to power the calc). Heck, I've plugged in more "dangerous" sources into my soundcard than this. Smile

Anyway, I captured some waveforms for testcn2.86p last night, and it appears that it sends the jamming signal for about 10.3ms, releases clock for about 230us, and then pulls down both lines for 125us for the byte guard (phase 3 of the byte-level protocol), but after that both clock and data are high for about 6.6ms, after which it starts the jamming phase for the next frame. Somehow it's not sending the bytes properly, or at all. I'll look into this today if I can.
Sounds great, glad you were able to make some progress towards debugging. On IRC I offered to send you and Lionel the reference z80 implementation if you want to take a look; let me know.
I increased the delays so I could see all of the link port transitions on my soundcard oscilloscope. I can see each bit being sent now, and it looks perfect, except I sent the payload length with the high bit set (whoops!). Let me fix that issue and then I'll upload a new version for someone to test.

By the way, in which order are the bits sent in each byte? The whitepaper is a little confusing on that point:
Quote:
An important note: although to function
correctly, the routine that CALCnet2.2 implements to transmit reverses the byte to be sent, the
most significant bit (MSB) of the byte is sent first, and the LSB last.

I chose to send the MSb first. Is that correct?

If it needs to be MSb first, then I think that's another problem with Lionel's code. He has a ror.b instruction which pushes the least-significant bit from the byte into the carry flag (and the comment above the ror.b disagrees with the instruction Smile).
Right, definitely looks like an error in my code (since the MSB is supposed to be sent first, I remember asking for confirmation), well spotted Smile
Fixing it does not enable the 84+SE to detect the 89T, however...

If you have the sound card set up as an oscilloscope and the adapter (which I don't have at the moment, although I have the items scattered in various places), maybe you'll be able to help with debugging the TI-68k version before I get the output from the oscilloscopes at work (I talked about it with a teacher colleague before leaving for today) Very Happy
It us indeed highest bit first. The link port port data bit on the TI-83 series is in a low bit, which is why I mention that I flip the byte and then rotate right one bit at a time.
Lionel Debroux wrote:
Right, definitely looks like an error in my code (since the MSB is supposed to be sent first, I remember asking for confirmation), well spotted Smile
Fixing it does not enable the 84+SE to detect the 89T, however...

If you have the sound card set up as an oscilloscope and the adapter (which I don't have at the moment, although I have the items scattered in various places), maybe you'll be able to help with debugging the TI-68k version before I get the output from the oscilloscopes at work (I talked about it with a teacher colleague before leaving for today) Very Happy


I ran your code in TiEmu, and it crashes with an Address Error (black bar on the top) when I press any key to quit. It does this on the TI-89 and TI-92+. I have the latest OS from the TI site (2.09). I'm a little hesitant to put it on my real 92+ if it's going to crash every time like that.

What adapter are your referring to? If you mean an adapter cable to plug the calculator into the soundcard, I actually just have a 3.5mm stereo cable plugged into the soundcard, my TI link cable plugged into the TI-86, and 3 alligator clip cables connecting the two. Yay alligator clips! Very Happy

KermMartian wrote:
It us indeed highest bit first. The link port port data bit on the TI-83 series is in a low bit, which is why I mention that I flip the byte and then rotate right one bit at a time.


Thanks for confirming. So flipping the byte was just an implementation detail as a kind of optimization, then?
Quote:
I ran your code in TiEmu, and it crashes with an Address Error (black bar on the top) when I press any key to quit. It does this on the TI-89 and TI-92+. I have the latest OS from the TI site (2.09). I'm a little hesitant to put it on my real 92+ if it's going to crash every time like that.

Oh, really ? It has never crashed here, either on TIEmu or the real 89T HW4 or 89 HW2 Confused
Yeah, if it crashes for you on TIEmu, you'd probably better not send that to your calculator. But I don't understand why...

Quote:
What adapter are your referring to? If you mean an adapter cable to plug the calculator into the soundcard, I actually just have a 3.5mm stereo cable plugged into the soundcard, my TI link cable plugged into the TI-86, and 3 alligator clip cables connecting the two. Yay alligator clips! Very Happy

I must have a male 3.5mm stereo jack with a wire, and I do have a female 2.5mm stereo jack, which I could solder together... but they're 75 km away from the place where I work during the week Sad


Re-posting the latest version:

Code:
#define USE_TI89
#define USE_TI92P
#define USE_V200

#define MIN_AMS 100
#define OPTIMIZE_ROM_CALLS
#define SAVE_SCREEN

#include <alloc.h>
#include <cert.h>
#include <flash.h>
#include <graph.h>
#include <intr.h>
#include <inttypes.h>
#include <kbd.h>
#include <link.h>
#include <peekpoke.h>
#include <stdio.h>
#include <string.h>
#include <system.h>

static volatile uint32_t int1_ticks = 0;
static volatile uint32_t int5_ticks = 0;
static INT_HANDLER saved_int_1;
static INT_HANDLER saved_int_5;

static uint8_t frame[30];

__attribute__((__regparm__(2))) void send_frame(uint8_t * ptr asm("%a0"), uint16_t len asm("%d0"));

//! New AUTO_INT_1 handler: count ticks, and execute AMS's handler, which handles keyboard reading.
DEFINE_INT_HANDLER(MyInt1)
{
    int1_ticks++;
    ExecuteHandler(saved_int_1);
}

//! New AUTO_INT_5 handler: count ticks, but don't execute the AMS timers and battery checker at a non-default rate.
DEFINE_INT_HANDLER(MyInt5)
{
    int5_ticks++;
    send_frame(frame, 13);
    //ExecuteHandler(saved_int_5);
}

// Notes:
// * clock -> tip
//   * SET bit 0 of 60000E to pull down
//   *     bit 2 of 60000E SET means pulled down.
// * data -> ring
//   * SET bit 1 of 60000E to pull down
//   *     bit 3 of 60000E SET means pulled down.

// Length is currently limited to 255 bytes.
asm("

.set CLOCK_WRITE_BIT, 0
.set DATA_WRITE_BIT,  1
.set CLOCK_READ_BIT,  2
.set DATA_READ_BIT,   3

| Write clock
.macro PULLDOWN_CLOCK
    bset    #CLOCK_WRITE_BIT,(%a1)
.endm

.macro PULLUP_CLOCK
    bclr    #CLOCK_WRITE_BIT,(%a1)
.endm

| Write data
.macro PULLDOWN_DATA
    bset    #DATA_WRITE_BIT,(%a1)
.endm

.macro PULLUP_DATA
    bclr    #DATA_WRITE_BIT,(%a1)
.endm


| Write clock and data at the same time
.macro PULLDOWN_BOTH
    st.b    (%a1)
.endm

.macro PULLUP_BOTH
    sf.b    (%a1) | clr.b (%a1)
.endm

.macro PULLUP_CLOCK_PULLDOWN_DATA
    move.b  #0x2,(%a1)
.endm

.macro PULLDOWN_CLOCK_PULLUP_DATA
    move.b  #0x1,(%a1)
.endm


| Read clock / data / both
.macro READ_CLOCK
    btst    #CLOCK_READ_BIT,(%a1)
.endm

.macro READ_DATA
    btst    #DATA_READ_BIT,(%a1)
.endm

.macro READ_BOTH dest
    move.b  (%a1),dest
.endm


.macro SEND_BIT
| Clock high for 17 µs, low for 35 µs, high for 52 µs
| Data = input bit for 52 µs, high for 52 µs
    bclr    #CLOCK_WRITE_BIT,%d2
    move.b  %d2,(%a1)
    moveq   #19,%d0
0:
    dbf     %d0,0b

    bset    #CLOCK_WRITE_BIT,%d2
    move.b  %d2,(%a1)
    moveq   #39,%d0
1:
    dbf     %d0,1b

    PULLUP_BOTH
    move.b  %d2,(%a1)
    moveq   #59,%d0
2:
    dbf     %d0,2b
.endm

    .text
    .even
    .globl send_frame
send_frame:
    move.w  %d3,-(%sp)
| Direct link I/O wire access
    lea     0x60000E,%a1

| Compute checksum
| TODO: compute checksum incrementally !
    move.w  %d0,%d3
    lea     12(%a0),%a0
    sub.w  #12+1,%d0  | len
| TODO: handle improper lengths
    moveq   #0,%d1  | val
    moveq   #0,%d2  | accumulator for checksum
loop_checksum:
    move.b  (%a0)+,%d1
    add.w   %d1,%d2
    dbf     %d0,loop_checksum

| Write checksum in little-endian form
    move.b  %d2,(%a0)
    lsr.w   #8,%d2
    move.b  %d2,1(%a0)

| Jamming phase: ~9.8ms
    PULLDOWN_CLOCK_PULLUP_DATA
| dbf timings: 10 CPU clock cycles when cc false and branch taken, i.e. <~ 1 us @ ~12 MHz.
| We need to loop ~12K times.
    move.w  #12000-1,%d0
loop_jamming:
    dbf     %d0,loop_jamming

    PULLUP_BOTH

| Get back to the beginning of the frame
    suba.w  %d3,%a0
| Add checksum length
    addq.w  #2-1,%d3

loop_send_bytes:
    bsr.s   send_byte
    dbf     %d3,loop_send_bytes

| TODO for directed frames: actually listen for the checksum...
|    move.w  #(182+60+50+8*(20+40+60)),%d0
|loop_wait_checksum:
|    dbf     %d0,loop_wait_checksum

| TODO: ACK/NAK the checksum if it's a directed frame
|    lea     ack(%pc),%a0
|    bsr.s   send_byte

    move.w  (%sp)+,%d3
    rts


    .text
    .even
send_byte:
    move.w  %d3,-(%sp)
| Both high for 52 + 110 µs
    PULLUP_BOTH
    move.w  #182-1,%d0
loop1_byte:
    dbf     %d0,loop1_byte

| Both low for 52 µs
    PULLDOWN_BOTH
    moveq   #60-1,%d0
loop2_byte:
    dbf     %d0,loop2_byte

| Both high for 43 µs
    PULLUP_BOTH
    moveq   #50-1,%d0
loop3_byte:
    dbf     %d0,loop3_byte

    move.b  (%a0)+,%d1

    moveq   #8-1,%d3
loop_send_bit:
| From bit 7 (MSB) down to bit 0 (LSB)
    rol.b   #1,%d1  | Leftmost bit goes to C
| We want the data line to have the same state as the input bit, so we want the DATA_WRITE_BIT to be not(input bit)
| if carry clear (i.e. input bit is 0) then pull down (i.e. set bit) else pull up (i.e. clear bit)
| if carry set   (i.e. input bit is 1) then pull up (i.e. clear bit) else pull down (i.e. set bit)
| Sxx is [if condition xx = true then 0xFF -> dest else 0 -> dest]

    scc     %d2
    SEND_BIT
    dbf     %d3,loop_send_bit

    move.w  (%sp)+,%d3
    rts

|
|ack:
|    .byte   0xAA
|    .even

");

//! Where all the fun starts...
void _main(void) {
    short saved_int5_rate = PRG_getRate();
    HANDLE cert_handle;
    unsigned long cert_len;
    CFILE context;
    CERT_FIELD field;
    unsigned short i;
    uint8_t calc_id[5];


    int1_ticks = 0,
    int5_ticks = 0;

    // Retrieve unique calculator ID, the proper (but large and slow) way.
    // The improper way, with very little chance of false positives, would be searching for 0A 15 <5 bytes> 0A 2D 40.
    FL_getCert(&cert_handle, &cert_len, FALSE);
    if (cert_handle != H_NULL && cert_len > 0) {
        unsigned char *ptr = HeapDeref(cert_handle);

        copen (&context, ptr, cert_len);
        // Root cert field of the Certificate memory.
        if (cfindfield (&context, 0x330, &field)) {
            copensub (&context, &field);
            // Serial number field.
            if (cfindfield (&context, 0xA10, &field)) {
                copensub(&context, &field);
                memcpy(calc_id, context.Pos, 5);
            }
        }

        HeapFree(cert_handle);
    }

    saved_int_1 = GetIntVec(AUTO_INT_1);
    SetIntVec(AUTO_INT_1, MyInt1);
    saved_int_5 = GetIntVec(AUTO_INT_5);
    SetIntVec(AUTO_INT_5, MyInt5);

    // Disable all linking interrupts
    pokeIO(0x60000C, 0x60);

    // Setup frame
    memcpy(frame, calc_id, 5); // Sender ID
    _memset(frame+5, 0, 5);    // Receiver ID, 0 for broadcast
    frame[10] = 0x01;          // Length, little endian 0x0001
    frame[11] = 0x00;
    frame[12] = 0x01;          // Data: 0x01: first part of the handshake used in Flourish & Netpong.

    // Let's fire AUTO_INT_5 at ~109.95 Hz (very close to the 110 Hz used by CALCnet)
    /*PRG_setStart(257 - 149);
    PRG_setRate(0);
    // Check AUTO_INT_5 frequency against AUTO_INT_1 frequency (well-defined to 256 Hz on HW1).
    while (!(_rowread(0xF000))) {
        if ((int1_ticks % 1024) == 0) {
            printf_xy(0, 0, "%" PRIu32 " %" PRIu32, int1_ticks, int5_ticks);
        }
    }

    PRG_setRate(saved_int5_rate);*/

    while (!(_rowread(0xF000)));

    SetIntVec(AUTO_INT_1, saved_int_1);
    SetIntVec(AUTO_INT_5, saved_int_5);

    OSLinkReset();
    GKeyFlush();
}



Compiled with a Makefile that reads as follows:

Code:
$ cat Makefile

all: calcnet.c
<tab>tigcc -Os -Wall -W -Wwrite-strings -Wshadow -mregparm=5 -fomit-frame-pointer -ffunction-sections -fdata-sections -fmerge-all-constants --optimize-code --cut-ranges --reorder-sections --merge-constants -Wa,--all-relocs -mno-bss -o calcnet calcnet.c
This version/Makefile doesn't crash in TiEmu. If I can find my 92+ and some batteries for it, I'll test out your code and check it out on my "oscilloscope". I still need to get mine working with the proper timings and such.
christop wrote:
KermMartian wrote:
It us indeed highest bit first. The link port port data bit on the TI-83 series is in a low bit, which is why I mention that I flip the byte and then rotate right one bit at a time.


Thanks for confirming. So flipping the byte was just an implementation detail as a kind of optimization, then?
That's correct. My apologies that I caused some confusion with that. Smile
I debooged and fine-tuned the timing in my calcnet driver. Apparently you have to give the link port a bit of time after changing the bit level (especially after releasing a line) before reading it next, otherwise it will give a false low reading.

Also, the timings are far different from what I calculated them to be. Do you know if the "OUT (LINKPORT),A" and "IN A,(LINKPORT)" instructions take longer on the TI-8x calcs than the Z80 docs say they should? The docs I have say they each take 11 cycles.

Anywho, here's my second test program:

http://www.mediafire.com/file/5q04fhk7ywo2zzd/testcn2.86p

Let me know if this one works.

I didn't change the jamming signal length, so let's see if that has any effect. It's actually only about 10.3ms long, since it starts about 0.8ms after an interrupt (10.3+0.8=11.1, which is the length of 2 interrupts). Given the different interrupt rate of the 83+/84+, it'll probably work out anyway.
Oooh, you don't have a second calculator to test it with? Sad I think I only have my TI-85 at home; my TI-86 is AWoL uptown somewhere.
No, sadly, I only have a TI-86 and a TI-92+. I had a TI-89 for about a year in high school but someone stole it. Mad That calculator was teh awexome.

Besides, I don't have the receiver code debugged yet, so it's kind of moot. I want to make sure my code works with the "official" DCS code on a network.
I can test it tomorrow when I have some time, its a little late today for me to be pulling out all my calcs to play around with.
TheStorm wrote:
I can test it tomorrow when I have some time, its a little late today for me to be pulling out all my calcs to play around with.
Very much appreciated, Jonimus. Smile I checked, and an 86 is indeed missing from my current collection. I hope to be able to remedy that sad state of affairs sooner rather than later.
Neither the 84+SE running OS 2.41, nor the 83+SE running OS 1.18, can detect the 86 running the testcn2 program, in 1:1 connection through a working cable.
Dots are moving at the bottom left of both screens, though.
Which dots specifically, Lionel, and using which program? On the 84+/83+ calculators, they're organized into three groups of 8 pixels at the bottom-left of the screen, and one set of 8 at bottom-right.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
» Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9  Next
» View previous topic :: View next topic  
Page 5 of 9
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement