6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Sep 20, 2024 5:54 pm

All times are UTC




Post new topic Reply to topic  [ 27 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sat Mar 16, 2019 6:00 am 
Offline

Joined: Wed Mar 13, 2019 9:41 pm
Posts: 20
I finally figured out how to get the C compiler working but I've hit another weird problem. I have the following code:
Code:
void monitor_putchar(char ch);

void main() {
   const char *str = "\nHello there\n";
   int n;
   
   for(n = 0; n < 5; n++)
      monitor_putchar(str[n]);
}

void monitor_putchar(char ch) {
   asm {
      lda %%ch
      jsl $00:e04b
   }
}

void IRQHandler() {}
This prints out the first 5 bytes of that string to the console. It works fine as long as the limit of the for loop is 9 or less. If I make it 10 or more, the thing seems to go into an infinite loop or something as soon as I run it.

And it's not the loop either. If I unroll the loop, the first nine invocations of monitor_putchar work, the tenth breaks everything. Same if the assembly is inlined, same if it's outputting the same character each time.

Nine times it works. Ten it doesn't. I don't even know where to start.

(Except that I kind of want to smash this board with a hammer and go back to the Z80)


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 16, 2019 7:27 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
Can you get an assembly listing of the resultant code? Can you disassemble the binary?

In your routine which contains inline assembly, where is the parameter value 'ch' to be found? What's the calling convention? It is certain that there's an implicit return and stack clearup at the end of the routine?


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 16, 2019 8:31 am 
Offline

Joined: Fri Apr 15, 2016 1:03 am
Posts: 139
My 1st guess is that you have overlaid some monitor variables. How do you tell the C compiler & linker what memory is available for use?
The Mensch monitor uses $00..$c0 & $100..$1c0 & the return stack is in $1c0..$1ff. See the Mensch monitor listing (download from WDC) for more detail.
The 1st monitor variables starting at $00 are managing the serial I/O buffers & the default buffers are 10 bytes long.

I assume your program is running in 6502 emulation mode.
If sharing with the monitor is a problem, it is possible to relocate your direct page (zero page on the 6502) to some other place in zero bank. This isn't completely 6502 compatible though.

I assume IRQhandler() is not used. If you do replace an active interrupt routine & your routine doesn't service the source of the interrupt, when your routine returns, the interrupt request will still be active & your routine will be called again & again.

If you put the definition of monitor_putchar before main, the extra monitor_putchar template definition wouldn't be needed.


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 16, 2019 1:46 pm 
Offline

Joined: Mon Sep 14, 2015 8:50 pm
Posts: 110
Location: Virginia USA
Hi randrews,

put_char() returns no errors but calls send_byte_to_pc() which will return carry set on error if I'm reading it right. And they are putting characters in a buffer while the monitor irq routines empty the buffer if that makes sense. See here for a text file of the monitor rom listing that I made:

viewtopic.php?f=4&t=4513

I think it's a little easier to read than the pdf.

Cheers,
Andy


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 16, 2019 2:50 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
randrews wrote:
This prints out the first 5 bytes of that string to the console. It works fine as long as the limit of the for loop is 9 or less. If I make it 10 or more, the thing seems to go into an infinite loop or something as soon as I run it.

So, what you're suggesting is that if you call the jsl $00:e04b 10 times in a row, it goes in to a loop?

Code:
    clc
    xce
    sep #$20
    LONGA OFF
    lda #$41   ; A
    jsl $00:e04b
    jsl $00:e04b
    jsl $00:e04b
    jsl $00:e04b
    jsl $00:e04b
    jsl $00:e04b
    jsl $00:e04b
    jsl $00:e04b
    jsl $00:e04b
    jsl $00:e04b
    lda #$42    ; B
    jsl $00:e04b

Not only will you not see the B printed, you'll only see 9 A's?

Edit: this may well be a handshake issue of some kind.
Quote:
LDX #10 ;SET BUFFER SIZES
STX SINCNT0
STX SINCNT1
STX SINCNT2
STX SINCNT3
STX SOUTCNT0
STX SOUTCNT1
STX SOUTCNT2
STX SOUTCNT3

All of the output buffers are set to 10.

I assume the monitor works? You can connect and dump memory (something that should readily use up more than 10 characters...)


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 16, 2019 7:10 pm 
Offline

Joined: Wed Mar 13, 2019 9:41 pm
Posts: 20
whartung: Sorry, I didn't explain this well. In your example I wouldn't see anything, no As or B. If you remove the B then I'd see nine As. And yes, the monitor can print as many characters as it wants. In fact if I make a program that prints nine characters I can run it over and over again without a reset.

leepivonka: I don't think I'm overwriting anything it uses, nothing in my program ends up on that page, my code segment is at 2000 and data at 3000, and the startup code (which is from c0l.obj, I didn't write it) is 7d00. I have to include IRQHandler because if I don't it complains that the symbol is undefined. I can't find anything about what it should actually DO though.

handyandy: I am almost certain you've got the right answer. I tried this last night right after I posted my question, I replaced the calls with putchar with calls to send_byte_to_pc. Suddenly I was still seeing only 9 characters... but no crash, the remaining characters just didn't show up. So I bet what I have to do is, call send_byte_to_pc repeatedly until I get a call that returns with the carry bit clear. There's probably a 10-char buffer of stuff it's sending over serial and once it fills that buffer put_chr breaks until it clears.


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 16, 2019 7:29 pm 
Offline

Joined: Wed Mar 13, 2019 9:41 pm
Posts: 20
And I spoke too soon. I replaced my monitor_putchar code with this:
Code:
void monitor_putchar(char ch) {
   asm {
            lda %%ch
   send_loop:   jsl $00:e063
            bcs send_loop
   }
}
And now I have the exact same problem I had when using put_chr: try to output more than 9, and it outputs nothing. Output 9 or fewer, and it works fine.

Take out the bcs and the behavior is, try to output more than 9, the first 9 are output and that's it. But it doesn't freeze.

So I now think the problem is: if you fill up the output buffer then something is supposed to fire to empty it, only it's not. Does anyone have an example working C program for this thing?


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 16, 2019 9:52 pm 
Offline

Joined: Mon Sep 14, 2015 8:50 pm
Posts: 110
Location: Virginia USA
send_byte_to_pc fills the buffer; irqat3, the ISR to transmit over UART port 3 is supposed to be emptying the buffer. Are you using hardware handshake or XON/XOFF?

I don't have an example working C program...


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 16, 2019 9:55 pm 
Offline

Joined: Wed Mar 13, 2019 9:41 pm
Posts: 20
I don't have anything for a handshake, my entire code is above. I'm trying to use the USB serial thing hooked to the monitor ROM.

So if that interrupt handler is supposed to empty it, maybe the problem is that I'm implementing IRQHandler with an empty function. But I can't find anywhere what's actually supposed to go there, and nothing in lib/ seems to provide that symbol.


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 16, 2019 10:45 pm 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
Why not implement your own polled output routine for now and replace with something cleverer later?

In my SXB-Hacker I use this for UART output.
Code:
; Wait until the last transmission has been completed then send the character
; in A.

                public  UartTx
UartTx:
                pha                             ; Save the character
                php                             ; Save register sizes
                short_a                         ; Make A 8-bits
                pha
                lda     #(1<<1)<<(2*UART)
TxWait:         bit     UIFR                    ; Has the timer finished?
                beq     TxWait
                pla
                sta     ARTD0+2*UART            ; Transmit the character
                plp                             ; Restore register sizes
                pla                             ; And callers A
                rts                             ; Done

You would need to the core of this in your C function.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 16, 2019 10:57 pm 
Offline

Joined: Wed Mar 13, 2019 9:41 pm
Posts: 20
I'm sorry, I don't know enough yet to understand what that code is doing. What are UIFR, UART, and ARTD0?


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 16, 2019 11:49 pm 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
UIFR and ARTD0 are constants for hardware register addresses. UART is a 0-3 value which determines which of the UARTs to use. I think the WDC monitor uses 3.

You could the routines in C, something like
Code:
const far unsigned char *pUIFR = 0x00DF48;
const far unsigned char *pARTD3 = 0x00DF77;

void putchar (char ch)
{
  while ((*pUIFR & 0x80) == 0x00) ;
  *pARTD3 = ch;
}

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 17, 2019 12:22 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
This seems to compile into reasonable looking code
Code:
#define UIFR      ((volatile unsigned char * far) 0x00df48)
#define ARTD3     ((volatile unsigned char * far) 0x00df77)

void putchar (char ch)
{
  if (ch == '\n') putchar ('\r');

  while ((*UIFR & 0x80) == 0x00) ;
  *ARTD3 = ch;
}

void puts (const char *pStr)
{
  register char ch;

  while (ch = *pStr++) putchar (ch);
}

int main (int argc, char **argv)
{
    puts ("Hello World\n");

    return (0);
}

It could be more efficiently coded in assembler but that can always be done later.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 17, 2019 12:38 am 
Offline

Joined: Wed Mar 13, 2019 9:41 pm
Posts: 20
Oh man, thank you so much, that's the first piece of example C code for this thing I've seen, and it worked!

I had to modify it a little bit, their compiler still doesn't link anything without me defining an IRQHandler function, but since that code doesn't use the IRQ handler (which I guess the monitor ROM does) then it works.

I think I'm going to return this board anyway though. The documentation is horrible, and if it took this much trouble to get a Hello World going then I'm expecting everything to be a hassle with this. I'll just use a normal 65c02 for my project.

Edit: except maybe not. The more I think about it the entire problem has been, well, anything that came out of WDC. If I use cc65 and don't try to call the monitor rom for anything, then writing code for this thing may be reasonable. I assume that stuff like the addresses you're poking in that code, you found by just reading the datasheet for the chip?


Top
 Profile  
Reply with quote  
PostPosted: Sun Mar 17, 2019 12:45 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
Scott and I wrote some documentation for the 265SXB here:

https://github.com/scotws/265SXB-Guide

Neither of us program the board in C. We use assembler. I have a 1MB SRAM expansion card I designed plugged into mine.

I rather like the board. Its much easier to work with than the 816SXB.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 27 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 13 guests


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 post attachments in this forum

Search for:
Jump to: