News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_McArti0

Does CPC use the R-efresh Z80A register?

Started by McArti0, 20:36, 11 March 24

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Does CPC use the R-efresh Z80A register for refresh Internal RAM?

Yes.
No.

robcfg

You can find them in the Gate Array Decapped thread: https://www.cpcwiki.eu/forum/index.php?msg=170713

Or in codedchip's repository: https://github.com/codedchip/AMSGateArray 

martin464

all this makes sense but one thing i don't understand - the video ram is being read full-time, 2 bytes each microsecond
the cpu is allocated its slot for the last part of that

how is a ram refresh squeezed into this time if the v-ram part is constant?

i thought i read somewhere it only refreshes if the z80 isn't accessing the ram I guess RD and WR being checked...
so it could be doing it selectively when cpu doesn't need memory access, if so i'm not sure how it handles it when external ram is paged in, maybe it switches that off too as part of it

so although it's under gate array control maybe it happens during the time the cpu usually has access to the ram?
CPC 464 - 212387 K31-4Z

"One essential object is to choose that arrangement which shall tend to reduce to a minimum the time necessary for completing the calculation." Ada Lovelace

McArti0

Quote from: martin464 on 14:32, 14 March 24how is a ram refresh squeezed into this time if the v-ram part is constant?
vram and all internal RAM is read at all time by GA based on the address from CRTC . This is enough for refresh all.
CPC 6128, Whole 6128 and Only 6128, with .....
NewPAL v3 for use all 128kB RAM by CRTC as VRAM
TYPICAL :) TV Funai 22FL532/10 with VGA-RGB-in.

andycadley

Reading a row of RAM is sufficient to refresh that row, when the GA doesn't need to read the row (because we're in the border area) it just refreshes it instead. Thus the RAM is always refreshed and the Z80 mechanism doesn't need to be involved.

If you set the GA up in such a way that it won't refresh the RAM itself, then it pretty much down to whether the Z80 ends up reading sufficient rows often enough to keep them all refreshed, because it's own mechanism isn't connected to the RAM.

SerErris

I think we turning in circles now. All that has been extensively explained in the posts before.

The RAM is organized (depending on the ram) in 127 or 256 rows and 512 or 256 columns.

The refresh of the ram works if you put one of the row addresses to the adress bus and then strobe  the RAS signal.
The RAM in CPC is connected in a way, that the lowest 8 bit actually form the row address. 

So in worst case it takes 256 µs for the gate array to read all row addresses. (2/µs). 

about 2/3 of a µs (10/16µs) is used by the GA to read two bytes and the other 6/10s are available to the CPU to work with memory. 

There is no additional refresh as the read from the gate array and the way how refresh works for RAM is enough to refresh everything 16times of the required time. So that includes the full 64/128kb of RAM. 

Interestingly - the gate array does not read all the ram, it only reads the 4k addressed by the CRTC. The rest is never read, and esp the second bank of the 6128 is actually never read. It just gets the same RAS signals and therefore is getting refreshed the same way, but it never gets CAS signals from GateArray.  Some expections - bank switching.
Proud owner of 2 Schneider CPC 464, 1 Schneider CPC 6128, GT65 and lots of books
Still learning all the details on how things work.

McArti0

#55
Quote from: SerErris on 22:15, 14 March 24I think we turning in circles now.
Ok. Lets go far. So how does CPC use the R-efresh register?

https://github.com/Bread80/CPC6128-Firmware-Source/blob/main/6128ROM.disasm

It's like timer?

;; sample edge and check for escape
;; L = bit-sequence which is shifted after each edge detected
;; starts of as &55 (%01010101)

;; check for escape
2b3d 06f4      ld      b,&f4 ;; PPI port A
2b3f ed78      in      a,(c) ;; read keyboard data through PPI port A (connected to PSG port A)
2b41 e604      and     &04 ;; escape key pressed?
;; bit 2 is 0 if escape key pressed
2b43 c8        ret     z


;; precompensation?
2b44 ed5f      ld      a,r   ;;<<<<<<<<<<<<<<<<<<<<<<<  A,R <<<<<<<<<<<<<<<<<<<<<<<<<<<<<

;; round up to divisible by 4
;; i.e.
;; 0->0,
;; 1->4,
;; 2->4,
;; 3->4,
;; 4->8,
;; 5->8
;; etc

2b46 c603      add     a,&03
2b48 0f        rrca ;; /2
2b49 0f        rrca ;; /4

2b4a e61f      and     &1f ;;

2b4c 4f        ld      c,a

2b4d 06f5      ld      b,&f5 ; PPI port B input (includes cassette data input)

;; -----------------------------------------------------
;; loop to count time between edges
;; C = time in 17us units (68T states)
;; carry set = edge arrived within time
;; carry clear = edge arrived too late

2b4f 79        ld      a,c ; [1] update edge timer
2b50 c602      add     a,&02 ; [2]
2b52 4f        ld      c,a ; [1]
2b53 380e      jr      c,&2b63          ; [3] overflow?

2b55 ed78      in      a,(c) ; [4] read cassette input data
2b57 ad        xor     l ; [1]
2b58 e680      and     &80 ; [2] isolate cassette input in bit 7
2b5a 20f3      jr      nz,&2b4f         ; [3] has bit 7 (cassette data input) changed state?

;; pulse successfully read

2b5c af        xor     a
2b5d ed4f      ld      r,a              ;;<<<<<<<<<<<<<<<<<<<<<<<  R,A <<<<<<<<<<<<<<<<<<<<<<<<<<<<<

2b5f cb0d      rrc     l ; toggles between 0 and 1

2b61 37        scf     
2b62 c9        ret     

;; time-out
2b63 af        xor     a
2b64 ed4f      ld      r,a                      ;;<<<<<<<<<<<<<<<<<<<<<<<  R,A <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
2b66 3c        inc     a ; "read error a"
2b67 c9        ret     

;;========================================================================================
;; write data byte to cassette
;; A = data byte
2b68 d5        push    de
2b69 1e08      ld      e,&08 ;; number of bits
2b6b 57        ld      d,a

2b6c cb02      rlc     d ;; shift bit state into carry
2b6e cd782b    call    &2b78 ;; write bit to cassette
2b71 3003      jr      nc,&2b76         

2b73 1d        dec     e
2b74 20f6      jr      nz,&2b6c         ;; loop for next bit

2b76 d1        pop     de
2b77 c9        ret     

;;========================================================================================
;; write bit to cassette
;;
;; carry flag = state of bit
;; carry set = 1 data bit
;; carry clear = 0 data bit

2b78 ed4be8b1  ld      bc,(&b1e8)
2b7c 2aeab1    ld      hl,(&b1ea)
2b7f 9f        sbc     a,a
2b80 67        ld      h,a
2b81 2807      jr      z,&2b8a          ; (+&07)
2b83 7d        ld      a,l
2b84 87        add     a,a
2b85 80        add     a,b
2b86 6f        ld      l,a
2b87 79        ld      a,c
2b88 90        sub     b
2b89 4f        ld      c,a
2b8a 7d        ld      a,l
2b8b 32e8b1    ld      (&b1e8),a

;; write a low level
2b8e 2e0a      ld      l,&0a ; %00001010 = clear bit 5 (cassette write data)
2b90 cda72b    call    &2ba7

2b93 3806      jr      c,&2b9b          ; (+&06)
2b95 91        sub     c
2b96 300c      jr      nc,&2ba4         ; (+&0c)
2b98 2f        cpl     
2b99 3c        inc     a
2b9a 4f        ld      c,a
2b9b 7c        ld      a,h
2b9c cd002b    call    &2b00 ; update crc

;; write a high level
2b9f 2e0b      ld      l,&0b ; %00001011 = set bit 5 (cassette write data)
2ba1 cda72b    call    &2ba7

2ba4 3e01      ld      a,&01
2ba6 c9        ret     


;;=====================================================================
;; write level to cassette
;; uses PPI control bit set/clear function
;; L = PPI Control byte
;;   bit 7 = 0
;;   bit 3,2,1 = bit index
;;   bit 0: 1=bit set, 0=bit clear

2ba7 ed5f      ld      a,r                    ;;<<<<<<<<<<<<<<<<<<<<<<<  A,R <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
2ba9 cb3f      srl     a
2bab 91        sub     c
2bac 3003      jr      nc,&2bb1         ;

;; delay in 4us (16T-state) units
;; total delay = ((A-1)*4) + 3

2bae 3c        inc     a ; [1]
2baf 20fd      jr      nz,&2bae         ; [3]

;; set low/high level
2bb1 06f7      ld      b,&f7 ; PPI control
2bb3 ed69      out     (c),l ; set control

2bb5 f5        push    af
2bb6 af        xor     a
2bb7 ed4f      ld      r,a                              ;;<<<<<<<<<<<<<<<<<<<<<<<  R,A <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
2bb9 f1        pop     af
2bba c9        ret     
CPC 6128, Whole 6128 and Only 6128, with .....
NewPAL v3 for use all 128kB RAM by CRTC as VRAM
TYPICAL :) TV Funai 22FL532/10 with VGA-RGB-in.

SerErris

#56
need to delete to rework
Proud owner of 2 Schneider CPC 464, 1 Schneider CPC 6128, GT65 and lots of books
Still learning all the details on how things work.

SerErris

;********************************** Read Byte
                                    ; OUT: A: Byte
                                    ;      CY=0 for error
                                    ;      A: Error number
29B0    D5          PUSH    DE
29B1    1E 08       LD      E,08        ; Counter for 8 bits
29B3    2A CE B8    LD      HL,(B8CE)   ; Time limit/edge flag
29B6    CD D4 29    CALL    29D4        ; Wait for 1st edge of the bit
29B9    DC DD 29    CALL    C,29DD      ; Is it okay? Wait for 2nd edge
29BC    30 0D       JR      NC,29CB     ; Error?
29BE    7C          LD      A,H         ; Time counter limit
29BF    91          SUB     C           ; Subtract current time counter to get bit
29C0    9F          SBC     A           ; A=$FF for 1-bit
29C1    CB 12       RL      D           ; Rotate bit into byte
29C3    CD 90 29    CALL    2990        ; Set check-w. according to bit
29C6    1D          DEC     E           ; Bit counter
29C7    20 EA       JR      NZ,29B3     ; More bits?
29C9    7A          LD      A,D         ; Read byte
29CA    37          SCF                 ; CY=1 for no error
29CB    D1          POP     DE 
29CC    C9          RET                

;**********************************  Wait for next edge, ESC t.
                                    ; IN/OUT: L: Edge flag
                                    ;              L=$55 for negative edge
                                    ;              L=$AA for positive edge
                                    ;              (OUT for the other edge)
                                    ;      OUT: C: Time counter
                                    ;           CY=0, Z=1, A=0 for abort
                                    ;           CY=0, Z=0 for error then: A: Error number
29CD    06 F4       LD      B,F4        ; PIO, Port B
29CF    ED 78       IN      A,(C)       ; Keyboard feedback of the 8th row
29D1    E6 04       AND     04          ; Isolate ESC bit
29D3    C8          RET     Z           ; ESC pressed?

;********************************** Wait for next edge
                                    ; IN/OUT: L: Edge flag
                                    ;              L=$55 for negative edge
                                    ;              L=$AA for positive edge
                                    ;              (OUT for the other edge)
                                    ;      OUT: C: Time counter
                                    ;           CY=0, Z=0 for error then: A: Error number
29D4    ED 5F       LD      A,R         ; Time since last edge
29D6    C6 03       ADD     03 
29D8    0F          RRCA                ; Generate corresponding time counter value
29D9    0F          RRCA               

29DA    E6 1F       AND     1F
29DC    4F          LD      C,A         ; Put it as time counter into C

;********************************** Wait for 2nd edge in the bit
                                    ; IN/OUT: L: Edge flag
                                    ;              L=$55 for negative edge
                                    ;              L=$AA for positive edge
                                    ;              (OUT for the other edge)
                                    ;      C: Time counter
                                    ; OUT: CY=0, Z=0 for error then: A: Error number

29DD    06 F5       LD      B,F5        ; PIO, Port B
29DF    79          LD      A,C         ; Time counter
29E0    C6 02       ADD     02          ; Increment
29E2    4F          LD      C,A         ; Set again
29E3    38 0E       JR      C,29F3      ; Carry? Then time is too long -+
29E5    ED 78       IN      A,(C)       ; Load input bit from cassette  |
29E7    AD          XOR     L           ; Invert depending on edge flag |
29E8    E6 80       AND     80          ; Isolate input bit             |
29EA    20 F3       JR      NZ,29DF     ; Not the expected level?   ----+
29EC    AF          XOR     A           ;   Reset time counter          |
29ED    ED 4F       LD      R,A         ;   Reset register              |
29EF    CB 0D       RRC     L           ; Edge flag for next edge       |
29F1    37          SCF     A           ; CY=1 for no error             |
29F2    C9          RET                                                 |
;Error Handling                                                         |
29F3    AF          XOR                 ;   Reset time counter      <---+
29F4    ED 4F       LD      R,A         ;   Reset register
29F6    3C          INC     A           ; Error number = 1, CY=0
29F7    C9          RET              

;********************************** Output byte
                                    ; IN : A: Byte
                                    ; OUT: CY=0 for error; A: Error number

29F8    D5          PUSH    DE 
29F9    1E 08       LD      E,08        ; Counter for 8 Bits
29FB    57          LD      D,A         ; Byte
29FC    CB 02       RLC     D           ; Next bit
29FE    CD 08 2A    CALL    2A08        ; Write to tape
2A01    30 03       JR      NC,2A06     ; Error?
2A03    1D          DEC     E           ; Bit counter
2A04    20 F6       JR      NZ,29FC     ; More bits?
2A06    D1          POP     DE 
2A07    C9          RET                

;********************************** Write bit to tape
                                    ; IN : CY: Bit
                                    ; OUT: CY=0 for error; A: Error number.

2A08    ED 4B D0 B8 LD      BC,(B8D0)   ; Previous time value/correction value
2A0C    2A D2 B8    LD      HL,(B8D2)   ; Main time value to L
2A0F    9F          SBC     A           ; A=$FF if 1-bit
2A10    67          LD      H,A         ; Save for check-word
2A11    28 07       JR      Z,2A1A      ; Bit=0?
2A13    7D          LD      A,L         ; Otherwise, main time value
2A14    87          ADD     A           ; *2 as 1-bit is twice as long
2A15    80          ADD     B           ; Add correction value
2A16    6F          LD      L,A         ; As time value
2A17    79          LD      A,C         ; Time value of the previous bit
2A18    90          SUB     B           ; Subtract correction value
2A19    4F          LD      C,A         ; Reset
2A1A    7D          LD      A,L         ; New time value
2A1B    32 D0 B8    LD      (B8D0),A    ; Save for the next bit
2A1E    2E 0A       LD      L,0A        ; Port C b5 (WR DATA) cleared
2A20    CD 37 2A    CALL    2A37        ; Delay and output
2A23    38 06       JR      C,2A2B      ; No error?
2A25    91          SUB     C           ; Error time-original time value
2A26    30 0C       JR      NC,2A34     ; Error time too large?
2A28    2F          CPL                 ; Original time value-error time
2A29    3C          INC     A           ; (Magnitude of the difference)
2A2A    4F          LD      C,A         ; Set as new time value
2A2B    7C          LD      A,H         ; Flag for 0/1-bit
2A2C    CD 90 29    CALL    2990        ; Set check-word according to bit
2A2F    2E 0B       LD      L,0B        ; Port C b5 (WR DATA) set
2A31    CD 37 2A    CALL    2A37        ; Delay and output
2A34    3E 01       LD      A,01        ; Error number for "Write error a"
2A36    C9          RET                

;********************************** Output next half wave
                                    ; IN : L=$0A for negative edge
                                    ;      L=$0B for positive edge
                                    ;      C: Time value

2A37    ED 5F       LD      A,R         ; Divide refresh counter by 2
2A39    CB 3F       SRL     A           ; Subtract desired time value since the last half-wave
2A3B    91          SUB     C           ; Has the time already elapsed?
2A3C    30 03       JR      NC,2A41     ; If not, remaining time
2A3E    3C          INC     A           ; Otherwise, remaining time
2A3F    20 FD       JR      NZ,2A3E     ; Delay
2A41    06 F7       LD      B,F7        ; PIO, Control register
2A43    ED 69       OUT     (C),L       ; Set/clear Port C/b5
2A45    F5          PUSH    AF          ; Error flag
2A46    AF          XOR     A           ; Reset time counter
2A47    ED 4F       LD      R,A         ; Reset register
2A49    F1          POP     AF          ; Error flags
2A4A    C9          RET                

Okay, I checked a very good german fully commented ROM listing for the CPCs. The addresses are different (because they do the full comment on the 464 and only comment on differences for the other two models), but that is the exact same function.

So this is the tape read and write routines and R is used as a counter for correct timing. 

This is only possible because the CPC never uses anything the R register contains, so they can use it as a counter.

So they set it to 0 (reset operation as in 29ED) and then use it as a counter.

The only thing they need to achieve is, that both edges are the same time. So they use R to have a counter. As they are running the same code, it will have the same time IF the signal from tape is correct. 

How does it work:
The start to wait for an edge (which is a change of signal).
Then they reset the timer
Then they wait for the next edge and store the time (LD A,R)
Then they reset the time
Then they wait for the next edge. 

This is one bit. And the edge timer is pretty much constant during that operation, however each bit sets the timings new. So the duration of a high and low flank is recorded.

That is important, as a high bit only takes half the time of a low bit. (the Pulse frequency is exactly 2x for high to low).
Proud owner of 2 Schneider CPC 464, 1 Schneider CPC 6128, GT65 and lots of books
Still learning all the details on how things work.

rpalmer

It would appear they are using the R register as a timing control. This makes sense as it is normally unaffected by anything external to the Z80, also the 1/300th interrupt is seen as insufficient for timing to be used for speedy bit transfers.
Think about that as at 1/300th interrupt only allows for 300 bits/sec where as using the R register could get a far high bit rate.

McArti0

except that R doesn't tick tok evenly. 
CPC 6128, Whole 6128 and Only 6128, with .....
NewPAL v3 for use all 128kB RAM by CRTC as VRAM
TYPICAL :) TV Funai 22FL532/10 with VGA-RGB-in.

andycadley

Quote from: McArti0 on 00:50, 16 March 24except that R doesn't tick tok evenly.
No, but it does tick predictably, especially if interrupts are disabled (as I assume they would be while tape loading). If you know what instructions are executing, R is a viable timing mechanism for something like this.

SerErris

Quote from: McArti0 on 00:50, 16 March 24except that R doesn't tick tok evenly.
That is true, but does not matter in this particular example, as a tick is running the code segment and a tock is running the same code segment again. So this is both the exact same commands in exact same order, so the time of both code segments will be the same.

But R is not good as a universal timer. You either need to count cycles and then ensure that both are the same with the same number of M1 cycles, or as in here, have the exact same code run again.

The more I read the ROM comment, the more I am impressed on what those guys have achieved.

Is it actually known how has written the ROM?
Proud owner of 2 Schneider CPC 464, 1 Schneider CPC 6128, GT65 and lots of books
Still learning all the details on how things work.

Bread80

This article on The Register gives a lot of detail about all aspects of development.

https://www.theregister.com/2014/02/12/archaeologic_amstrad_cpc_464/

SerErris

Quote from: Bread80 on 12:51, 17 March 24This article on The Register gives a lot of detail about all aspects of development.

https://www.theregister.com/2014/02/12/archaeologic_amstrad_cpc_464/
Thank you for this interesting article. It slipped on my by now.

I am thinking of archiving it to archive.org. Reaching out to the register
Proud owner of 2 Schneider CPC 464, 1 Schneider CPC 6128, GT65 and lots of books
Still learning all the details on how things work.

Powered by SMFPacks Menu Editor Mod