How is LDIR affecting memory?

Posts about Z80 programming that do not relate to a particular computer
Post Reply
Stroopwafel
Posts: 4
Joined: Tue Sep 15, 2020 8:48 am

How is LDIR affecting memory?

Post by Stroopwafel » Thu Sep 17, 2020 6:40 am

Hi,

I'm new here, thank you for giving me access to this forum. I'm following your lessons here (and watching the videos) but I'm already stuck at lesson two. Here LDIR is used to loop over memory addresses to set them to 0 to clear the screen. In the cheat sheet LDIR is explained as

Code: Select all

Load HL, DE
Until b=0:
  inc HL
  inc DE
  dec BC
HL and DE are pointed to two memory locations. So when they are both increased as long as BC has room to decrease, they are just pointing to the next memory location. How is this affecting that memory?

User avatar
akuyou
Posts: 545
Joined: Mon Apr 22, 2019 3:19 am
Contact:

Re: How is LDIR affecting memory?

Post by akuyou » Sun Sep 20, 2020 7:22 am

Hey there! thanks for the question!

I think you've slightly misread the cheatsheet (unless there's a misprint version out there)

The first line should be:
LD (DE),(HL)

Notice the brackets ()?

HL means the value in HL
(HL) means the byte at address HL
so "LD (DE),(HL)" copies one byte from address HL to address DE

Note there's no such command as LD (DE),(HL) - it's just explaining what the command does... but the principal is the same as the real commands:
LD A,(HL)
LD (DE),A
Chibi Akuma(s) Comedy-Horror 8-bit Bullet Hell shooter! // 「チビ悪魔」可笑しいゴシックSTG ! // Work in Progress: ChibiAliens

Interested in CPU's :Z80,6502,68000,6809,ARM,8086,RISC-V
Learning: 65816,ARM,8086,6809

Stroopwafel
Posts: 4
Joined: Tue Sep 15, 2020 8:48 am

Re: How is LDIR affecting memory?

Post by Stroopwafel » Tue Sep 22, 2020 8:34 am

I think you've slightly misread the cheatsheet (unless there's a misprint version out there)

The first line should be:
LD (DE),(HL)
Yes, that must be it. I already understood that (HL) and (DE) are pointers to an address in memory, but I thought that for instance INC HL would differ from INC (HL) in that the former would increase the pointer, while the latter would change the value of the memory address pointed to. So my confusion was that I thought only the pointers, not the values they pointed to, were changed by LDIR.

From the cheat sheet:

Code: Select all

LDIR Load(DE),(HL)... INC HL... ; no brackets around the argument for INC
INC r  increase r by one.
So can I conclude that when r is HL or DE, INC r actually increases the value in (HL)/(DE), not the value in HL/DE?
Last edited by Stroopwafel on Sun Sep 27, 2020 1:11 pm, edited 1 time in total.

Stroopwafel
Posts: 4
Joined: Tue Sep 15, 2020 8:48 am

Re: How is LDIR affecting memory?

Post by Stroopwafel » Tue Sep 22, 2020 9:08 am

So, I thought I could test this with this little piece of code:

Code: Select all

org &8000
LD DE,1
LD (&4000),DE	; load 1 into memory at &4000
LD HL,4000	; make HL a pointer to &4000
INC HL		; HL now holds &4001
RET
After this, HL has changed to &4001, DE holds 1, as does the memory address &4000.

When I change that line into INC (HL), HL holds &4000, DE holds &4001 and the memory address &4000 has increased to 2. Which is what I would expect. So, there is the expected difference between INC HL and (HL), which leads me back to my original question. :-S

Stroopwafel
Posts: 4
Joined: Tue Sep 15, 2020 8:48 am

Re: How is LDIR affecting memory?

Post by Stroopwafel » Wed Apr 21, 2021 8:01 am

akuyou wrote: Sun Sep 20, 2020 7:22 am so "LD (DE),(HL)" copies one byte from address HL to address DE
I probably should have read that better. ;-.-

SerErris
Posts: 1
Joined: Wed Nov 02, 2022 11:25 am

Re: How is LDIR affecting memory?

Post by SerErris » Sat Nov 26, 2022 2:55 pm

I think your understanding is still wrong.
So can I conclude that when r is HL or DE, INC r actually increases the value in (HL)/(DE), not the value in HL/DE?
No that is actually wrong.

LDIR does the exact same as this piece of code:

Code: Select all

;copy &100 Bytes from &4000 -> &8000
LD BC,&100       ; load BC with number of bytes (BC = byte count)
LD HL,&4000      ; load HL with source
LD DE,&8000      ;load DE with destination           (DE = destination)
PUSH AF          ;save AF before copying the data. (actually this is not what the LDIR command does - it just does not work with A register).
loop:
    LD A,(HL)    ;load memory location indicated by HL to A
    LD (DE),A    ;load A to memory location indicated by DE
    inc HL       ;increment HL
    inc DE       ;increment DE
    dec BC       ;decrement BC
    jr nz,loop   ;loop if BC>0
    
 pop AF          ;restore AF
 
Now we have copied the &100 bytes from &4000 to &8000.
Shorter way but same result is:

Code: Select all

LD BC,&100
LD HL,&4000
LD DE,&8000
LDIR
LDIR does exactly the following in human readable words:

1. Copy Byte from memory location pointed to by HL into memory location pointed to by DE
2. Increment both HL and DE (the pointers, not the memory locations)
3. Decrement BC
4. loop if BC>0

So the increment commands in the pseudo code of the LDIR description increment the registers and have no impact on the memory itself. The copy in step 1 has the impact to the memory.

LDIR is just a fast way to copy memory regions with a fixed length.

Lets compare the number of memory cycles the CPU will use for both operations (1 memory cycle equals one NOP operation or 4 CPU clock ticks) (actually on individual systems this is different but for CPC it is 4 clocks = 1 memory cycle). Everything is actually counted in NOPs.

So for variant number 1 (the full code) the time in the loop + the push/pop is the following:

Code: Select all

CMD                             NOPs
PUSH AF                          4      
loop:
    LD A,(HL)                    2
    LD (DE),A                    2
    inc HL                       2
    inc DE                       2
    dec BC                       2
    jr nz,loop                   2
    
 pop AF                          3
 
 TOTAL                          19
Time for one LDIR iteration: 6 NOPs.

So LDIR is 3 times faster than the other way.

Now back to the example from the lesson:

Clear Screen CPC:

Code: Select all

LD BC,&4000              ;screen size
LD HL,&C000              ;screen start
LD DE,&C001              ;next cell
LD (HL),&00              ;clear the first memory cell.
LDIR                     ;copy the first cell to the next cell, increment HL and DE, decrement BC, loop if BC>0
What it does?
It puts in just 00 in the first byte of the screen (source) and then copies this byte to the next cell, then increase both pointers and do this over and over again.

If you use WinAPE, you can run that in the debugger and watch the memory location.

To observe this better, you want to use this:
Fill Memory with &FF

Code: Select all

LD BC,&100          ;Number of cells to fill
LD HL,&5000         ;start address
LD E,L              ;copy HL to DE
LD D,H                    
INC E               ;now DE point to one cell above HL
LD (HL),&FF         ;setup the first cell with &FF
LDIR                ;copy the first cell to the next cell, increment HL and DE, decrement BC, loop if BC>0
So set your debugger to &5000 and step through the code and see what it does.

Post Reply

Return to “General Z80 Programming”