• Please review our updated Terms and Rules here

Help with z80asm, compiles correctly, but address ranges corrupt Hex file output.

pocoaust

Member
Joined
Jun 1, 2022
Messages
14
G'day again, the micromation is haunting me......

I have a uart attached to a doubler card, that uses only 2 addresses to work. The hardware takes care of the control, so I only need to read FE0AH to get the Status registers, and either read or write to FE02H to send receie data. I have tried moding a simple monitor source, which compiles on its own address's, but when i change the code to address the above ports, it complies but the outputted file is corrupt and missing the end.

Here is some sample code:
CONOUT: PUSH AF ; push AF into stack
LD A,(0FE0AH) ; Read from Control Port
AND 04H ; logical AND transmit ready 01h or 00000001 bin
JP Z,CONOUT+1 ; jump to address
POP AF ; POP FROM THE STACK BACK TO AF
LD (0FE02H),A ; write to port A
RET ; Return

as i said, if I compile it says all okay, but HEX file is corrupt. If I compile original with low address, it works. If i try to use IN or out with the high address ranges it gives me

Z80ASM Copyright (C) 1983-86 by SLR Systems Rel. 1.32 #AB1234
JADOR/H
JADOR.Z80 - Byte Out of Range Line 01375 IN A,(KBDST)JADOR.Z80 - Byte Out of Range Line 01382
CONIN: IN A,(KBDST)End of file Pass 1
2 Error(s) Detected.
1984 Absolute Bytes. 161 Symbols Detected.

Any help appreciated......
 
Am I missing something? The valid I/O port range for an IN our OUT instruction is 0-255. (0-ff hex). Looks to me like your first section of code is using memory-mapped I/O, not port I/O.

To belabor the point, Intel/Zilog x80 and x86 CPUs use a different address space for the I/O instructions, unlike, say, the 6502 or 6800. On the 8080 or z80, the size of this address space is only 256 bytes.
 
Am I missing something? The valid I/O port range for an IN our OUT instruction is 0-255. (0-ff hex). Looks to me like your first section of code is using memory-mapped I/O, not port I/O.

To belabor the point, Intel/Zilog x80 and x86 CPUs use a different address space for the I/O instructions, unlike, say, the 6502 or 6800. On the 8080 or z80, the size of this address space is only 256 bytes.
Thanks Chuck,

my learning curve is step and painful.. so how do I read 8 bits from a port at FE0A Hex, and send 8 bits to FE02 Hex. I have been trying to load the value in the 16bit registers and passing them to LD, but getting nowhere.

this is the description of the UART function from the manual. I read it at existing on the Z80 bus with the status port at 0FE0AH and the Data port at 0FE02H.

Can I get data by simply loading the 16 bit address into a 16 bit register and using ld pointing to that address to move the data to the accumulator? Or am i just completely mistaken ;-/ The /WR and /pbin are hardwired to the bus, and baud rate selection is via jumper block.

8-4 READ AND WRITE CONTROL

The read/write control generates strobes that operate the read, write, control, and status latches on the DOUBLER's internal data bus. Its inputs are address lines A0-A2, /WR, /PDBIN, and I/O. This circuit consists of two, eight wide data distributors, lIC7D and lIC6D (74LSl38). Both of these are enabled by the I/O signal from the address decoder PROM. lIC6D and lIC7D are also enabled by /PDBIN and /WR, respectively, /PDBIN enables the read control; /WR enables the write control. Low active strobes generated by the read/write control memory map are listed in firmware as follows.

/WR /PDBIN
ADDRESS FE02H WRUART RDUART
FE0AH UARTSTAT

These strobes and their corresponding latches perform the following functions. WRCONT loads the drive control latch, 5IC10A (74S374), which operates the drive control lines. RDSTAT reads the drive status latch, 5ICIIA (74LS224), which contains the drive status lines. WRCLK writes the clock pattern to the sync mark latch, 2IC12D (74LS273). This value is then compared with the clock pattern from the SYNC FIELD HEADER to synchronize with the moving disk data. WRUART and RDUART write and read to the UART, 6IC13D (8251), used for the RS-232 serial interface. WRMRK is a strobe that writes an ID or a DATA MARK to the disk (depending on the status of the MRKA signal). 22 RDMRK is a strobe that holds the processor until a MARK is read (or the timeout triggers) in order to synchronize the byte sync counter. It is also used to clear the head load counter. WRMRKCRC and RDMRKCRC are strobes that perform the same functions as WRMRK and RDMRK, respectively, and also preset the cyclic redundancy check circuit. WRDATA and RDDATA these strobes act ivate the DISKWR and /DISK READ signals respectively. Addressing these ports transfers data to or from the disk on a byte by byte basis. WRCRC is a strobe used to gate the cyclic redundancy check" character into the serial data stream. SYNCPORT is a strobe used in the synchronization of the byte s yn c co un t e r • UARTSTAT accesses the control and status latches in the UART. Address line A2 is used to enable the DISKWR and /DISKREAD signals. All strobes listed above in the address range of FE04H - FE07H also transfer data to or from the disk when active. 8-5 THE UART The UART, 6IC13D (8251), is based at FE02H, and selected by the /RDUART and /WRUART signals from the READ/WRITE CONTROL. Address line A3 is connected to the UART C/D input, which accesses its control and status latch, based at FE0AH.
 
+1.

Yes, you appear to be (potentially) mixing memory-based I/O with I/O ports.

You also appear to have provided us with the source code for CONOUT and the assembler is complaining about CONIN.

Please explain exactly what you are trying to achieve?

Dave
 
Hi Dave,

I am trying to get a simple monitor to boot on a micromation M/NET machine. The Z64 cpu board has room for a monitor on a 2k eprom, but it doesn't have a UART to display console messages. The Doubler Floppy controller has a UART on it, and that is the details of how to address it in the above document.
I have come to the realisation that the address requires a 16 bit bit register to store the address and have been trying to figure out how to do this, but my assembly skills are near zero, so im bumbling though. I would love a simple bit of sample code that could start from F000 (eprom location) and output a string repeatedly to the uart on the doubler. I could then at least tell if my hardware repairs to the main system have worked. I would then use this as a basis for merging with some simple monitor examples. I have attached the manuals for the Z64 processor board and the Doubler Floppy controller with UART.

Any help would be great.

Thanks
Paul in Oz
 

Attachments

  • Micromation_DOUBLER_Operator_Manual_1980.pdf
    5.7 MB · Views: 3
+1.

Yes, you appear to be (potentially) mixing memory-based I/O with I/O ports.

May I humbly suggest that no he's not, and that maybe you are :) he's obviously using memory mapped I/O and pretty much says so in his question :)
I dont know which bit you have for tx ready, but in line 3 your AND and your comment dont align, one or the other is wrong.
Also, use another label instead of "xxxx + 1", its much clearer, costs nothing and avoids problems where the label you're referencing isnt yet resolved :)
Z80ASM also needs space for its symbol table and workspace, which typically would be in high memory, maybe you're overwriting it. I'm out & about at the moment
and cant remember offhand but there is probably a directive to move the symbol table, also some assemblers use PHASE or LOAD to assemble and ORG at different addresses
for use with ROMs etc, this can be used to avoid conflicts in occupied ram
Otherwise the code looks ok :)
Cheers
Phil
 
Last edited:
What is the value of KBDST? "IN A,( n )" and "OUT ( n ),A" only use an I/O port address in the range 0-255, so the error message is very probably correct. They are used for port-based I/O, not memory-mapped I/O.

EDIT: I hate those stupid auto-smilies
 
>>> May I humbly suggest that no he's not, and that maybe you are :) he's obviously using memory mapped I/O and pretty much says so in his question :)

I think I know what the OP has done. He has memory mapped I/O on his computer, but has used a monitor program source from another machine that has I/O based instructions for IN and OUT to convert for his machine. All that has (probably) been done is that the I/O 8-bit addresses for CONIN and CONOUT within the monitor have been exchanged for the 16-bit memory addresses for their hardware - hence the error.

The OP needs to replace the IN and OUT instructions with the LD instruction of course - but I was trying to teach the OP where the error came from in the first place rather than just tell them what to do...

Dave
 
Firstly, Thanks for all your help. I have been on a step learning curve so got as far as trying to implement the bit address in the 16bit registers, but still cant get it to compile. I am worried the compiler is stopping me because this is protected space for CP/M? Here is where my code is now....

If someone has any suggestions, alternate compiler, better code... that would be great. I think i am trying to paint a masterpiece, but only know how to use a crayon at the moment ;-)

CONOUT: PUSH AF ; push AF into stack
LD HL,0
LD HL, 0FE0AH ; load address into 16bit stack
LD A,HL ; Read from Control Port
AND 04H ; logical And transmit ready 01h or 00000001 bin
JP Z,CONOUT+1 ; jump to address
POP AF ; POP FROM THE STACK BACK TO AF
LD HL,0
LD HL, 0FE02H ; Load address in 16 bit stack
LD HL,A ; write to port A
RET ; Return
;
CONIN: LD HL,0
LD HL, 0FE0AH ; load address into 16bit stack
LD A,HL
AND 02H ; logical And receive ready 02h or 00000002 bin
JP Z,CONIN
LD HL,0
LD HL, 0FE02H ; Load address in 16 bit stack
LD A,HL ; read from dataport into A
AND 7FH ; only chars up to 7F
CP 61H ; 'a' to '|' ? If so, A-=20H for capitalization
JP C,ECHO
CP 7CH
JP NC,ECHO
SUB 20H ;subtract
 
You don't have to use HL to load the A register from memory--and the LD HL,0 in your code is completely superfluous.

Bits are numbered from the low-order position. So if you're checking bit 0, your bit mask is 1. If you're checking bit 1, your mask 2. If checking bit 7, you mask is 128 or 80h.

In other words, you might code the first routine as:

Code:
 CONIN:  
 CONIN2:  LD     A, (0FEA0H)  ; keyboard status
        AND  02H
        JR     Z, CONIN2            ; Loop if character not available
        LD     A, (0FEA2H)         ; get keyboard character.
....
I have several observations. First is that you need to know bit about the instruction set and how the CPU works. The web is full of this kind of information--for assembly syntax, you might start here: https://www.msx.org/wiki/Z80_Assembler_for_Dummies
My next obsevation is to write meaningful comments to say what you're doing, not how the instruction works.
Finally, using +n or -n addressing is a very bad practice. If the code gets modified, that +1 might need to become +2 and it will come back to bite you. The assembler gives you labels--use them!
Note also, that I used the Z80 relative branch, which is a byte shorter than the JP variety--and can be relocated if need be without doing address correction.
 
Last edited:
Pardon me if I am wrong (I have not used Z80 assembly in a long time): But doesn't his reading the port into A and then popping AF overwrite what he just read?
 
It does indeed, which is why I cut my example off where I did. The OP needs to understand the architecture. I also started with his CONOUT routine, then changed my mind. My sample code now omits the PUSH at the start.
It's also worth noting that CP/M requires a "Console Status" routine. The simple way is to code the status routine, then call it from the "Console in" routine and input and return the character.
 
Last edited:
Pardon me if I am wrong (I have not used Z80 assembly in a long time): But doesn't his reading the port into A and then popping AF overwrite what he just read?
It does, but this code is in the CONOUT subroutine and that is exactly the desired effect.

We save the character to be output (in A) on the stack. Wait until the transmitter is free. Recover the character to be output from the stack. Finally, output the uncorrupted character to the transmitter.

Thanks for the information. You are currently being moderated, so your posts are being delayed. I see you have now reached 10 posts so you will be allowed to fly free now!

Chuck has given you the code for CONIN, just replicate the good practice into CONOUT.

There is a steep learning curve, but it does start to make sense eventually. Honest :).

Dave
 
Last edited:
I see where I went wrong here. In the CONOUT I wasn't seeing he was just reading the status port to make sure the character could be sent. Sorry about that.
 
Thanks guys... I implemented these changes, but i think I have the same issues..... Firstly I need a 16 bit Address, so from reading i need to load it into HL first then move it to the accumulator? This version below compiles on z80asm under CP/m but the output hex file is truncated near the end when it gets to this code. So I'm wondering if it is trying to load the 16 bit address straight to the accumulator, or it is the compiler not allowing access to these high address ranges.

Does anyone have a a running version of z80asm they could try to compile on and see if they get the same results. here is the code cut out. I will attach the modified monitor file as well.

Thanks again for your efforts... am reading assembly for dummies now ... ;-/

CONOUT: PUSH AF ; push AF into stack
LD A, (0FEA0H) ; Read UART Status register a FEA0H (16bit address required)
AND 04H ; logical AND the UART Status Byte transmit ready 01h or 00000001
JR Z, CONOUT ; Loop if Status byte says UART not ready
JP Z,CONOUT+1 ; jump to address?
POP AF ; POP FROM THE STACK BACK INTO A
LD (0FEA2H),A ; Write A byte to UART at OFEA2H (16bit address Required)
RET ; Return
;
CONIN: LD A, (0FEA0H) ; Read UART Status register a FEA0H (16bit address required)
AND 02H ; logical AND the UART (02H)/8251 RECEIVE READY = (0000 0002)B or (02)H
JR Z, CONIN ; Loop if character not available
LD A, (0FEA2H) ; read from UART dataport at 0FEA2 (16bit address required) into A
AND 7FH ; only chars up to 7F
CP 61H ; 'a' to '|' ? If so, A-=20H for capitalization
JP C,ECHO
CP 7CH
JP NC,ECHO
SUB 20H ;subtract

Truncated z80asm file end.... should finish at 7FFF.....

:10F7800008CDE9F6CB233E188F4FCDF1F610F5C921
:10F79000F5E5219FF77ECDF1F623FE03C295F742F2
:10F7A00041443A2003E1F1C9CDDCF63E10D36E218D
:10F7B0000000DB6EE610C2B2F7DB6FFB7723C3B24B
:05F7C000F7C324F176FF
:00000001FF
 
JR Z, CONOUT ; Loop if Status byte says UART not ready
JP Z,CONOUT+1 ; jump to address?
Um, that isn't going to work--the JR Z, is going to CONTOUT and keep pushing AF until you clobber something.

Look JR does the same as JP, but uses one less byte, So change the JR Z,CONOUT to JR Z,CONOUT+1 (although I detest this syntax--better to use another label) and drop the JP instruction.

In short, if you can do JP to some location within 127 bytes of the instruction, you can use a JR instead. JP is the long 8080 jump; JR is shorter Z80 jump.
 
The LD A,(xxxH) does EXACTLY what you want in a single instruction.

It uses the 16-bit address (xxxH), reads the 8-bit data value from the specified 16-bit address and stores this value into the A accumulator.

It always has done this, and always will do.

Your 'truncation' issue is something different.

Can you obtain a listing file from the assembler and post a ZIP file containing the listing file and your truncated hex file.

Just out of interest, what makes you think that it should finish at 7FFF?

Where does your program start also?

Dave
 
Last edited:
Thanks for everyones help. I have now got a working monitor that runs in emulator, but I have found the the 74s471 prom that controls the data lines to the s100 bus has a bad output. I will attempt to read the code, but as the bad output is on a data line, i will see how I go.

Thanks Again.
 
Back
Top