Home » RBC Forums » General Discussion » SIO Programming Help
SIO Programming Help [message #10300] |
Fri, 31 March 2023 20:18  |
protocall7
Messages: 20 Registered: October 2019
|
Junior Member |
|
|
Hey everyone! It's been quite a long time since I've posted, I hope everyone has been doing well!
I have recently got the hardware put together for a modular Z80 system that I've been designing for a number of years now. Currently it is a pretty simple system with a Z84C0010 processor running at 10mhz, 32K of ROM, 32K of RAM using A15 to select between them, and an SIO with its clocks driven by a 1.8432MHz oscillator.
After I got the board together and did a bit of initial probing around with my scope and logic analyzer and running test programs, I decided to start in on a driver for my SIO. So far I have written initialization routines, got transmit working very inconsistently, got receive working very inconsistently, then broke both of them when I attempted to set everything up to be interrupt driven.
My backplane board is set up for interrupt daisy-chaining on the peripheral ports, and I'd like to use IM 2 on this machine. I'll be honest that I'm pretty new to Z80 ASM, and the board is a new design so it's entirely possible that there is multiple bugs, and some may be in the hardware as well as the software. Since I had some functionality working, then broke it again, I *think* the issue lies in the software, but I have been staring at it for so long that its becoming a blur.
When I connect the serial card to a terminal via an FTDI cable with the current iteration of the code, I get either gibberish or nothing. After several power cycles or presses of the reset button, I can always make the machine produce a string of gibberish. Sometimes the machine will go into a loop spitting out unprintable chars, which tells me that one of the interrupts is probably firing, but I'm not sure which one at the moment. Pressing keys to send data to the SIO does *not* reliably produce output as I would expect, so I likely have a problem with my Interrupt Vector Table or ISRs as well.
I realize its a huge ask with a completely unknown machine and code, but would anyone mind taking a look to see if I've done anything glaringly wrong here? I'd be happy to provide schematics or any measurements that would be helpful if someone's willing to take a look.
https://gitlab.com/protoCall7/pz803-firmware
Any advice anyone could offer would be very greatly appreciated.
Thanks!
proto
|
|
|
|
|
Re: SIO Programming Help [message #10304 is a reply to message #10303] |
Sun, 02 April 2023 05:32   |
coredump
Messages: 33 Registered: January 2020 Location: Germany
|
Member |
|
|
Hi proto,
half success ;-)
protocall7 wrote on Sun, 02 April 2023 02:30
[...]
I've been going through the datasheets and my schematics to see if I can find any errors there, and noticed that I forgot to put a pull-up resistor on INT, so I suppose it could be floating and causing me all kinds of issues. I'm gonna take the machine out to my workshop and tack a pull-up resistor on there next and see if that helps.
proto
If You can not have the resistor at it's intended place right now You can instead shift the moment where interrupts become enabled just behind the printing of the signon string.
Enabling interrupts also means open doors for race conditions.
As an example: SIO_PORT_A_RTS_ON:
ld a, %00000101 ; point to WR5
out (SIO_CTRL_A), a ; send pointer to channel a
ld a, %11101010 ; enable the RTS bit
out (SIO_CTRL_A), a ; send the updated register
If an interrupt homes in here just after the first out instruction and tries to do something similar,
the order of -write register number - write register- would be disrupted and everything gets messed up.
BTW what is Your intention in setting/ resetting /RTS?
Detlef
|
|
|
|
Re: SIO Programming Help [message #10306 is a reply to message #10305] |
Mon, 03 April 2023 13:10   |
coredump
Messages: 33 Registered: January 2020 Location: Germany
|
Member |
|
|
Hi Peter,
protocall7 wrote on Sun, 02 April 2023 21:24Hi Detlef,
Unfortunately, it still prints "xxxxxx<xxxxxxxxx<x<xx<x<xxx" on startup.
It might sound funny but that's not too bad. It seems to send reliably exactly one string of data after every RESET.
RS232 tends to be somewhat cumbersome at times.
You may have brought out the lines from the SIO directly or You may have a RS232-driver IC between SIO and connector.
With driver IC on Your Card, the USB-RS232 thingie is required to also have a driver chip inside.
And vice versa. If unsure, an oscilloscope gives quick results. Or a DVM showing the static voltage level of a handshake output.
Checking Baud rate ans serial parameter settings on the PC side again might be good, too.
But if it would be my problem, as a first step I would hook up an oscilloscope to the Tx output of the SIO to find out what it sends. You should find out about the baud rate and by decoding the first bytes by hand about the contents.
This saves a lot of guess work.
Quote:
I was setting the RTS line because I wanted to utilize hardware flow control with the auto-enables, but found that my FTDI cable doesn't seem to be responding to the RTS with a CTS, nor setting RTS itself when it wants to send. I also discovered that I need to have the DCD lines pulled low for auto-enables to work, which I don't have on the current rev on the board. I was intending on pulling it low with a jumper wire, but haven't revisited hardware flow control yet. Is it possible that I'm causing myself issues by setting and unsetting that line?
The way you are describing it seems to be the way RTS/CTS are handled in synchronous communication.
The FTDI will not understand this.
There are several ways asynchronous handshake was realized. Your communications program on the PC should have a settings menue that offers something like RTS/CTS, XXXX, NONE.
With RTS/CTS, setting /RTS inactive signals the other side (on /CTS) to stop transmission.
Setting /RTS active again asks to resume communication. That's the whole trick.
The Z80-SIO doesn't support this in hardware. You may later implement it in software.
For the first tests I would recommend to set the handshaking on the PC to NONE to ignore the handshaking issue completely. A bridge from RTS to CTS on each side does, in a practical sense, the same.
Maybe a schematic etc. would allow us to be more helpful.
Detlef
|
|
|
|
Re: SIO Programming Help [message #10308 is a reply to message #10307] |
Mon, 03 April 2023 20:04   |
protocall7
Messages: 20 Registered: October 2019
|
Junior Member |
|
|
This is rather interesting! I put the TX on my scope's logic analyzer, and got a trace that decoded to x's and unprintable chars, but looked like it was going much slower than 115200. After playing with the decoder's baud rate for a bit, I discovered that the card is actually outputting valid serial data at 28800 baud. Looking at the code https://gitlab.com/protoCall7/pz803-firmware/-/blob/master/s io.asm#L209-210 I'm setting the first two bits of WR4 to 01, which the datasheet says corresponds to data x 16 = clock. With a 1.8432MHz clock input, that should be 115200 baud. I would have expected 28800 baud if I had those first two bits set to 11 for x64. Am I misinterpreting the datasheet?
Also there is no conversion happening between the SIO and the outputs. It's running at TTL levels with the chip directly connected to the connector.
[Updated on: Mon, 03 April 2023 20:48] Report message to a moderator
|
|
|
|
|
|
Re: SIO Programming Help [message #10313 is a reply to message #10312] |
Tue, 04 April 2023 22:45   |
protocall7
Messages: 20 Registered: October 2019
|
Junior Member |
|
|
The SIO is still connected in the slot that pulls IEI to +5. I have confirmed that handshaking is disabled in minicom, as well as using stty to be sure it's turned off.
I probed around with the scope for a bit, and discovered that when a key is pressed on the keyboard, it does cause the SIO to pull the INT line low, however the HALT LED remains lit, and the INT line stays low until I reset the computer. I am guessing its possible that something is going on with the interrupt acknowledgement, so the SIO never places a vector on the bus, or there is something going on with my SIO initialization or interrupt vector, causing the machine to jump off to some random address instead of to the appropriate ISR.
I've been reading through the datasheet to double check my SIO initialization, and found one place that I'm a bit confused. On write register 1, is D2 only available in port B, or is the datasheet saying that the vector that D2 affects is only available in port b? I have seen example code that sets port A's WR1 to 00011100, which would be interrupt on all rx chars, parity not a spec cond, spec conds affect vector. I was under the impression that I should be setting port A wr1 to 00011000 and port b wr1 to 00000100 in order to get RX interrupts with spec conds affecting the vector on port a.
Peter
|
|
|
Re: SIO Programming Help [message #10315 is a reply to message #10313] |
Wed, 05 April 2023 12:02   |
coredump
Messages: 33 Registered: January 2020 Location: Germany
|
Member |
|
|
Hi Peter,
protocall7 wrote on Wed, 05 April 2023 07:45
I probed around with the scope for a bit, and discovered that when a key is pressed on the keyboard, it does cause the SIO to pull the INT line low, however the HALT LED remains lit, and the INT line stays low until I reset the computer. I am guessing its possible that something is going on with the interrupt acknowledgement, so the SIO never places a vector on the bus, or there is something going on with my SIO initialization or interrupt vector, causing the machine to jump off to some random address instead of to the appropriate ISR.
Very well done!
You probed it, You made assumptions what might be the problem.
So tests and measurements would be on order.
But I would like to add an other possibility:
Since the WAIT-LED seems not to change brightness but it should if the CPU accepts interrupts and the INT-line is continuously active (so it would quickly leave the wait state), the interrupts might be disabled.
And in fact, it looks a little bit so:
We all see the EI-statement. It is placed after the startup-message-loop and before the DONE-label.
But the loop is never left at the end, instead of it jumps directly to DONE out of the middle (just over the head of the EI).
Maybe moving EI after DONE: fixes the main problem.
Quote:
I've been reading through the datasheet to double check my SIO initialization, and found one place that I'm a bit confused. On write register 1, is D2 only available in port B, or is the datasheet saying that the vector that D2 affects is only available in port b? I have seen example code that sets port A's WR1 to 00011100, which would be interrupt on all rx chars, parity not a spec cond, spec conds affect vector. I was under the impression that I should be setting port A wr1 to 00011000 and port b wr1 to 00000100 in order to get RX interrupts with spec conds affecting the vector on port a.
I'm clearly under the same impression. But You can test it before You change it back.
And maybe You want to have the 'unused' Interrupts print messages to know what goes on.
I guess there are different versions of the datasheet available.
I do peek into this one: http://www.hartetechnologies.com/manuals/Zilog/Zilog%20Z80-S IO%20Technical%20Manual.pdf
even if it was done before the CMOS version was available.
Detlef
|
|
|
Re: SIO Programming Help [message #10330 is a reply to message #10315] |
Wed, 03 May 2023 19:01   |
jayindallas
Messages: 110 Registered: June 2021
|
Senior Member |
|
|
A few suggestions on coding and the SIO
A). WHEN YOU SEE A MOUNTAIN OF PUSH AND POP, TRY TO MAKE IT A MOLEHILL IF POSSIBLE
This routine, SIO_PORT_A_WRITE, can be streamlined to something like the next code block. Stack operations are slow so you want to minimize them when you can. I'll include a trick for improving stack balance in the next section C). below.
=====================
CURRENT CODE LISTING:
=====================
SIO_PORT_A_WRITE:
pop bc ; Return Address is first off the stack
pop de ; Character stored in e
push bc ; Restore the return address to the stack for ret
push af ; save af register
ld a, e ; move the character to A
out (SIO_DATA_A), a ; send the character to the SIO
call SIO_A_TX_WAIT ; block until the character is sent
pop af ; restore af register
ret
=====================
STREAMLINED CODE LISTING:
=====================
SIO_PORT_A_WRITE: ; NOTE STACK ACTIVITY IS SLOW... AVOID WHEN OK
push af ; save af register
; Character stored in e
ld a, e ; move the character to A
out (SIO_DATA_A), a ; send the character to the SIO
call SIO_A_TX_WAIT ; block until the character is sent
pop af ; restore af register
ret
B). LETTING THE Z80 AND THE SIO WORK CONCURRENTLY
Your SIO_A_TX_WAIT routine is one way to assure that your TX buffer doesn't get overloaded; it waits for every character sent, to be transmitted. Very logical.
But devices like SIOs are somewhat autonomous and can let the Z80 do the minimum code to send a character on its way and get back to processing code while the SIO transmits from its buffer.
For example, if your code just wrote to the SIO buffer, and didn't wait for the complete character transmission, your overall code would run faster, however you might lose a character, after a while, when the SIO buffer is FULL and can't take another character for the buffer, but the routine writes it anyway. Whether the SIO overwrites the last character or fails to load it... either way its a problem of one lost character.
This thought sequence reveals a more efficient SIO TX driver. Its the buffer full status that is the only problem that the TX character routine has to worry about at this point.
So... before sending a character to the buffer, just make sure that buffer is not full, and if it is not full, then write the character into the buffer and return to other Z80 code. But if it is full, then you must wait until it is not full again, before you add another character into the SIO buffer. This doesn't slow down the Z80 as much and with fast baud rates it would certainly spend less time than waiting for each character to be sent before returning to other Z80 code.
To get even better performance with very little waiting, could be to implemented to have a driver for a ram buffer buffer between the message character string and the SIO buffer. This could be done with a background, timer interrupt routine to 'shovel' the characters from the ram message buffer into the SIO buffer instead. The trick becomes... how do you stop that ram buffer interrupt from wasting Z80 runtime particularly when there is nothing in the ram buffer for the SIO. That's probably a topic for another day.
Another method would be to use an interrupt that signals that the SIO has buffer space available or ready for another character and use that to shovel characters out of the ram buffer and into the SIO buffer. There are various ways to shut off the interrupt when the ram buffer is found to be empty but you might need to implement a sentinel flag to tell the interrupt not to access the ram buffer because the foreground code is updating it. If both foreground and interrupts are updating the ram buffer pointers etc, then the code has to be very good to avoid an entanglement. Sometimes a sentinel flag as suggested is a simple solution.
But seeing the possibilities and putting a useful thought in your 'tool-box' is a great technique for learning to code better and better; and that's how we all really learn. With that concept in your tool kit, you may see a similar problem and decide its time to try it. After a while your tool-box has a lot of useful tricks that makes assembly language coding easier and require less debugging.
C). TRICKS TO ATTAIN A NATURAL FEEL FOR STACK BALANCE
The hardest thing to do in coding assembly language is probably keeping the stack balanced. Maybe a good technique while attaining a natural feel for stack balance is to do something like this code example, to document the subroutine's stack activity:
When a subroutine does a PUSH or POP, just illustrate the subroutine's STACK change with the PUSH POP comments like below:
; Initialize SIO
INIT_SIO: ; QUICKLY RESET SIO SO INTERRUPTS DISABLED, THEN INIT
push af ; push the af register [STACK+AF]
ld a, %00011000 ; store channel reset command in a
di ; disable Z80 interrupt activity while resetting SIO{A,B}
out (SIO_CTRL_A), a ; reset SIO Channel A, interrupts disabled
out (SIO_CTRL_B), a ; reset SIO Channel B, interrupts disabled
ei ; enable Z80 interrupt activity
;-------------------------------; INITIALIZE SIO{A&B}
push bc ; push the bc register [STACK+AFBC]
push hl ; push the hl register [STACK+AFBCHL]
;
; REMEMBER SIO MUST INITIALIZE WR5 FIRST!!
;
; To receive or transmit data in the Asynchronous mode, the Z80 SIO must
; be initialized with the following parameters: character length, clock rate,
; number of stop bits, even or odd parity, interrupt mode, and receiver or
; transmitter enable.
;
ld a, CONF_STR_N ; load the number of config strings to a
ld hl, SIO_INIT_STR ; port first
CONFIG_LOOP:
ld c, (hl) ; address of SIO port in c
inc hl ; point at the length
ld b, (hl) ; length of byte string
inc hl ; point at the bytestring
otir ; send bytes
dec a ; decrement our counter and set flags
jr nz, CONFIG_LOOP ; jump back to the loop if not
pop hl ; pop hl [STACK+AFBC..]
pop bc ; pop bc [STACK+AF..]
pop af ; pop af [STACK+..]
ret ; return
[Updated on: Thu, 04 May 2023 09:07] Report message to a moderator
|
|
|
Re: SIO double register access requires disabled interrupts. How to minimize the disabled duration. [message #10345 is a reply to message #10304] |
Tue, 09 May 2023 13:18   |
jayindallas
Messages: 110 Registered: June 2021
|
Senior Member |
|
|
If you're using time critical interrupts, you want to minimize the time in which interrupts are disabled. These six alternatives illustrate how it helps to think about the impact of arbitrarily placed DI and EI interrupt instructions. Instead of using them as 'bookends' around a block of code, its minimizes the time the interrupts are disabled if you set things up before disabling interrupts. The six alternates show the progress of changes that make it quicker and quicker.
Six alternates to analyze:
;--------------------------------------------------------------------
; alternate 1: ; B T D; B is bytes of code, T is T-states, D is disabled duration T-states
di ; 1 4 0;
ld a, %00000101 ; 2 7 7; point to WR5
out (SIO_CTRL_A), a ; 2 11 11; send pointer to channel a
ld a, %11101010 ; 2 7 7; enable the RTS bit
out (SIO_CTRL_A), a ; 2 11 11; send the updated register
ei ; 1 4 4; intr off for 40 T-states
; ;== == ==
; Totals 10B,44T,40T :: Interrupts disabled for a duration of 40 T-states.
;--------------------------------------------------------------------
; alternate 2: ; B T D; B is bytes of code, T is T-states, D is disabled duration T-states
ld a, %00000101 ; 2 7 0; point to WR5
di ; 1 4 0;
out (SIO_CTRL_A), a ; 2 11 11; send pointer to channel a
ld a, %11101010 ; 2 7 7; enable the RTS bit
out (SIO_CTRL_A), a ; 2 11 11; send the updated register
ei ; 1 4 4; intr off for 33 T-states
; ;== == ==
; Totals 10B,44T,33T :: Interrupts disabled for a duration of 33 T-states.
;--------------------------------------------------------------------
; SPOILER ALERT: alternates 3&4 are equivalent, tiebreaker: alternate 3 wins on better documentation (binary values)
; alternate 3: ; B T D; B is bytes of code, T is T-states, D is disabled duration T-states
ld a, %00000101 ; 2 7 0; point to WR5
ld b, %11101010 ; 2 7 0; enable the RTS bit
di ; 1 4 0;
out (SIO_CTRL_A), a ; 2 11 11; send pointer to channel a
ld a,b ; 1 4 4; enable the RTS bit
out (SIO_CTRL_A), a ; 2 11 11; send the updated register
ei ; 1 4 4; intr off for 30 T-states
; ;== == ==
; Totals 11B,48T,30T :: Interrupts disabled for a duration of 30 T-states.
;--------------------------------------------------------------------
; SPOILER ALERT: alternates 3&4 are equivalent, tiebreaker: alternate 4 loses on lesser documentation (hex values)
; alternate 4: ; B T D; B is bytes of code, T is T-states, D is disabled duration T-states
ld hl, $05EA ; 3 10 0; point to WR5, then enable the RTS bit
ld a,h ; 1 4 0;
di ; 1 4 0;
out (SIO_CTRL_A), a ; 2 11 11; send pointer to channel a
ld a,l ; 1 4 4; enable the RTS bit
out (SIO_CTRL_A), a ; 2 11 11; send the updated register
ei ; 1 4 4; intr off for 30 T-states
; ;== == ==
; Totals 11B,48T,30T :: Interrupts disabled for a duration of 30 T-states.
;--------------------------------------------------------------------
; 30T-states with interrupt disbled, equivalent to alternates 3&4 but cost one byte more.
; alternate 5: ; B T D; B is bytes of code, T is T-states, D is disabled duration T-states
ld a, %11101010 ; 2 7 0; enable the RTS bit
ex af,af' ; 1 4 0;
ld a, %00000101 ; 2 7 0; point to WR5
di ; 1 4 0;
out (SIO_CTRL_A), a ; 2 11 11; send pointer to channel a
ex af,af' ; 1 4 4;
out (SIO_CTRL_A), a ; 2 11 11; send the updated register
ei ; 1 4 4; intr off for 30 T-states
; ;== == ==
; Totals 11B,49T,30T :: Interrupts disabled for a duration of 30 T-states.
;--------------------------------------------------------------------
; THE REAL WINNER!!!! 28T-states
; alternate 6: ; B T D; B is bytes of code, T is T-states, D is disabled duration T-states
ld hl, $05EA ; 3 10 0; point to WR5, then enable the RTS bit
ld c,SIO_CTRL_A ; 2 7 0; point to the sio.a.control register
di ; 1 4 0;
out (c),h ; 2 12 12;
out (c),l ; 2 12 12;
ei ; 1 4 4; intr off for 28 T-states
; ;== == ==
; Totals 11B,49T,28T :: Interrupts disabled for a duration of 28 T-states.
;--------------------------------------------------------------------
[Updated on: Fri, 12 May 2023 19:51] Report message to a moderator
|
|
|
Re: Some suggestions on "monitor.asm" [message #10346 is a reply to message #10345] |
Wed, 10 May 2023 14:48   |
jayindallas
Messages: 110 Registered: June 2021
|
Senior Member |
|
|
I've gone over the monitor.asm as currently listed of github/whatever.
I've pointed out a few BUGs, there were not many, but my intent is to make some useful comments or guidance for any coder reading this.
I understand that you may already know all of this, but some readers may be inspired by your code to try doing something like it, themselves. I explain some issues in details that might help some readers that may not have discovered it yet.
Those that have studied some computer science might be more inclined to think that using stack manipulations is better, but while true on modern PC and computers, its really not true on the early 8 bit computers.
Some of the things I suggested, like the PUSH&POP register saves and restores are arbitrary, opinions differ as to put them all in first and prune them out later. I did that too in the past. But when beginning to write a monitor for a new board, in might be best to keep it simple and uncluttered until you get it basically under control.
I use the comment text " ;j: " to make my comments in the comment area or along the left margin.
Left margin " ... " means a section of code or text has been removed from this listing.
;j: at 0000h
RST0: ;j: FOR A BETTER CHANCE OF CAPTURING RUNAWAY CODE AND REINITIALIZING AT 0003h
nop ;@0000h ;j: if runaway code rolls from top of memory into 0000h address...
nop ;@0001h ;j: after a last instruction framed from @{FFFC,FFFD,FFFE,FFFFh}...
nop ;@0002h ;j: it will be assured to be in-sync with opcode instruction by 0003h
;j: bytes of single instruction can be {4,3,2,1} byte(s) long
;j: worst case is 4 byte long instruction fetched at FFFF,0000,0001,0002h)
di ;@0003h ; disable interrupts to start off
jp START ; jump over the reset handlers and IVT
...
;j: at 0200h+
START:
;j: If I recall, its true that ld SP $0000 will make the actual top of stack occur at $FFFF
;j: as it decrements the stack address before pushing the first data onto the stack.
;See page 25, Z80 Tech Manual ;j: PUSH HL acts like: a) dec SP ;SP was 0000h, then 0000h -1 = FFFFh
;j: b) LD (SP),(H) ;LD (FFFFh),(H)
;j: c) dec SP ;SP was FFFFh, then FFFFh -1 = FFFEh
;j: d) LD (SP),(L) ;LD (FFFEh),(L)
;j:
;j: POP HL acts like: a) LD (L),(SP) ;SP is FFFEh, value in FFFEh copied into reg L
;j: b) inc SP ;SP was FFFEh, then FFFEh +1 = FFFFh
;j: c) LD (H),(SP) ;SP is FFFFh, value in FFFFh copied into reg H
;j: d) inc SP ;SP was FFFFh, then FFFFh +1 = 0000h
;j:
;j: Note: the stack never 'uses' 0000h for data UNLESS you pop it beyond your push count
;j: or RET from subroutines more times than you called subroutines
;j:
;j: When you POP data, the SP is already pointing at the LOW BYTE
;j: coders figure this out when they debug their stack and are surprised
;j: ld sp, $0000 ;j: would be the pro way to initializing, but only *IF* you have RAM at FFFFh :)
ld sp, $FFFF ; set the stack pointer
im 2 ; interrupt mode 2
ld a, INT_TABLE/256 ; store the MSB of the IVT addr in a
ld i, a ; load a to i
;j: at this point there are no register values to save, so no need to PUSH/POP them in INIT_SIO
call INIT_SIO ; set up the SIO
call STARTUP_MESSAGE ; send the startup message
ei ; enable interrupts
...
;
; Routine to send the startup message to Port A of the SIO
; Must call INIT_SIO first!
;
STARTUP_MESSAGE:
;j: no need for these PUSHes and POPs before returning. As the code grows, that may change.
;j: however the more you can eliminate unnecessary stack activity, the faster the code will run.
;j: in interrupt service routines its best to minimize internal CALL&RET / PUSH&POP unless protecting the registers you change.
;j: ISR PUSH&POP minimization often change registers you might use; if you PUSH BC for B only, you get C register for 'free'.
;j: and all ISRs would need to PUSH AF on entry and POP AF before return. Avoid another level of PUSH&POP AF. Use a free register
push af ; save af register
push bc ; save bc register
push hl ; save hl register
ld hl, STARTUP_STR ; load the address of the startup string to hl
STARTUP_MESSAGE_LOOP:
ld a, (hl) ; load the first char to a
or a ; is it a NULL?
jr z, MESSAGE_LOOP_END ; we're done if it is
;j: no need of register B because you don't need to PUSH BC
;j: and if you need to PUSH a single register of a register pair, its not necessary to change modify that register.
;j: A PUSH BC takes 11 T-states, a POP takes 10. But register loads from regisers are the fastest. LD E,C takes 4 T-states.
;j: If register E isn't used, you can LD E,C in only 4 T-states and call a routine that changes C without PUSH&POP
;j: where possible, that it better, but it might bite you when you rewrite a routine to add more features.
;j: Its a good idea to document which registers some subroutines change. It reduces debugging time.
;j: If code requires a register be unchanged before calling a subroutine, document that before the CALL. Helps debugging.
ld b, 0 ; not a NULL, zero out b
ld c, a ; store our char in c
;j: this PUSH is a BUG because will cause a STACK overrun and mess up pops and returns immediately
;j: no need to push a character on the stack, if you need to save it, copying it to an unused register or ram, is faster
push bc ; put the char on the stack
call SIO_PORT_A_WRITE ; call the write routine;
inc hl ; point to the next char in the string
jr STARTUP_MESSAGE_LOOP ; back to the beginning of the loop
MESSAGE_LOOP_END:
;j: no need for these PUSHes and POPs before returning. As the code grows, that may change.
;j: when you're doing an initial debug, optional clutter like this becomes additional code you have to consider as being the bug.
pop hl ; restore hl register
pop bc ; restore bc register
pop af ; restore af register
ret
;
; handler for unimplemented ISRs
;
NO_ISR:
ei ; enable interrupts
;j: RETI is only used as a RET for interrupt triggered CALLs
;j: This might create a interrupt mess. RET is the instruction to use here.
reti ; go back to what we were doing
SECTION DATA
STARTUP_STR:
db "PZ803 ROMMon v0.1\r\n"
db "(c) 2023 - Peter H. Ezetta\r\n", 0
|
|
|
Re: Some suggestions on "monitor.asm" [message #10371 is a reply to message #10346] |
Sun, 11 June 2023 13:53   |
phil_g
Messages: 32 Registered: November 2020
|
Member |
|
|
I would suggest testing for SIO busy first rather than waiting for completion afterwards, ie change
out (SIO_DATA_A), a ; send the character to the SIO
call SIO_A_TX_WAIT ; block until ***this*** character is sent
to
call SIO_A_TX_WAIT ; block until the ***previous*** character has completed
out (SIO_DATA_A), a ; send the character to the SIO and get on with your day...
This way you're not waiting for transmit to complete, leaving more processing time elsewhere, and chances are the previous one has completed anyway.
Cheers
Phil
[Updated on: Sat, 17 June 2023 05:33] Report message to a moderator
|
|
|
Re: SIO double register access requires disabled interrupts. How to minimize the disabled duration. [message #10379 is a reply to message #10345] |
Wed, 23 August 2023 09:04   |
etchedpixels
Messages: 333 Registered: October 2015
|
Senior Member |
|
|
jayindallas wrote on Tue, 09 May 2023 13:18:
; THE REAL WINNER!!!! 28T-states
; alternate 6: ; B T D; B is bytes of code, T is T-states, D is disabled duration T-states
ld hl, $05EA ; 3 10 0; point to WR5, then enable the RTS bit
ld c,SIO_CTRL_A ; 2 7 0; point to the sio.a.control register
di ; 1 4 0;
out (c),h ; 2 12 12;
out (c),l ; 2 12 12;
ei ; 1 4 4; intr off for 28 T-states
; ;== == ==
; Totals 11B,49T,28T :: Interrupts disabled for a duration of 28 T-states.
;--------------------------------------------------------------------
Not quite. Move the ei to before the second out( statement. An ei take effect one instruction after (so the 8080 ei ret with stuck IRQ flaw is fixable) otherwise your intr off time is actually 28 + whatever instruction follows which is a big difference.
|
|
|
|
Current Time: Tue Jul 15 12:35:36 PDT 2025
Total time taken to generate the page: 0.00808 seconds
|