Categories
cpm z80

CP/M programming info

Best links I’ve found:
bdos calls: http://www.shaels.net/index.php/cpm80-22-documents
https://www.seasip.info/Cpm/bdos.html


CPM3 for S100 http://www.s100computers.com/Software%20Folder/CPM3%20BIOS%20Installation/CPM3%20HDISK%20BIOS%20Software.htm
CPM3 https://forum.vcfed.org/index.php?threads/installing-cp-m-3-plus-on-a-home-built-z80-computer.61802/
ISIS/PLM https://www.retrotechnology.com/restore/isis.html

General info: http://primrosebank.net/computers/cpm/cpm_about.htm



Categories
z80

XModem for Z80

I wanted to implement a XModem to incorporate into the Z80 monitor program for my CPUVille Z80. It needed to be small to fit in the 2K ROM monitor, the whole thing ends up being 264 additional bytes. Here is the code for that:

; based on 6502 version http://www.6502.org/source/io/xmodem/xmodem-receive.txt
; re-coded by Tom Lovie 2023-03-23
; sub write_string and put_chr are already in ROM

monitor_warm_start:     equ     0450h

SOH     equ 01h         ; start block
EOT     equ 04h         ; end of text marker
ACK     equ 06h         ; good block acknowledged
NAK     equ 15h         ; bad block acknowledged
CAN     equ 18h         ; cancel (not standard, not supported)
CR      equ 0dh         ; carriage return
LF      equ 0ah         ; line feed
ESC     equ 1bh         ; ESC to exit

rbuff:  equ 0c00h       ; page aligned receive buffer
rbuffp: equ 0ch         ; page of receive buffer
sbuff:  equ 1000h       ; storage buffer to place received data
sbuffp: equ 10h         ; storage buffer page

        org 0800h

                call XModem
                jp monitor_warm_start

XModem:
                ld hl,msg
                call write_string
                ld hl,sbuff
                ld (ptr),hl     ; initialize the storage pointer
                ld a,01h
                ld (blkno),a    ; start at block number 1
StartX:         ld a,NAK        ; Start in CRC mode - no fallback
                call put_chr    ; send it
                ld a,00h        ; loop counter in a
                call GetByte    ; try to get a byte
                jr c,GotByte
                jr nc,StartX    ; if not try again

StartBlk:       ld a,00h        ; loop counter in a
                call GetByte    ; try to get a byte
                jr nc,StartBlk
GotByte:        cp ESC          ; want to quit
                ret z
                cp SOH          ; start of block?
                jr z,BegBlk
                cp EOT          ; end of text
                jr nz,BadCrc
                jr Done
BegBlk:         ld hl,rbuff     ; start hl at the receive buffer
GetBlk:         ld a,00h        ; 3 second window to receive char
GetBlk1:        call GetByte    ; get next char
                jr nc,BadCrc    ; just sending NAK
GetBlk2:        ld (hl),a       ; store the character in buffer
                inc hl          ; increment the buffer
                ld a,83h
                cp l            ; <01><FE><128 bytes><CHKSUM>
                jr nz,GetBlk    ; get 131 characters (0x83)
                ld l,00h        ; start at beginning of buffer 
                ld a,(blkno)    ; actual block number
                cp (hl)         ; sent block number
                jr z,GoodBlk1   ; block number is expected
                jr ErrorOut     ; error out of the xmodem routine
GoodBlk1:       xor 0ffh        ; compliment the actual block no
                inc hl
                cp (hl)         ; compare to second byte
                jr z,GoodBlk2   ; block number compliment 
                jr ErrorOut     ; error out of the xmodem routine
GoodBlk2:       ld h,rbuffp     ; point hl at the receive buffer
                ld l,81h        ; last byte
                ld a,00h        ; initialize a
CalcCrc:        add (hl)        ; compute running total start 82h
                dec l
                jr nz,CalcCrc
                add a,(hl)      ; do the block number as well
                inc a           ; because blockno + cpl = 255.
                ld l,82h        ; (hl) is the checksum
                cp (hl)
                jr z,GoodCrc
BadCrc:         call Flush      ; flush serial buffer
                ld a,NAK        ; and send
                call put_chr    ; a NAK
                jr StartBlk     ; restart the block
GoodCrc:        ld l,02h        ; hl is now pointing data
                ld de,(ptr)     ; de is now pointing storage
                ld c,80h        ; 128 bytes
                ld b,00h        ;
                ldir            ; copy the block
                ld (ptr),de     ; store the current pos
                ld a,(blkno)    ; load the block number
                inc a
                ld (blkno),a    ; store the block number back
                ld a,ACK        ; send ACK
                call put_chr
                jp StartBlk     ; get next block
Done:           ld a,ACK
                call put_chr
                call Flush
                ld hl,good      ; load success message
                call write_string
                ret

ErrorOut:       ld hl,err       ; print error message and exit
                call write_string
                call Flush      ; discard remaining buffer
                ret             ; return after fatal error

; subroutine to wait a set amount of time to get a byte
; Byte will be in A, destroys BC (delay loop)
GetByte:        ld b,a
                ld c,a
GetByteLoop:    call get_chr
                ret c           ; return if got chr (carry set)
                dec c
                jr nz,GetByteLoop
                dec b
                jr nz,GetByteLoop       ; delay loop
                or a                    ; clear carry flag
                ret

; subroutine to flush the receive buffer
; destroys A
Flush:          ld a,80h
                call GetByte
                jr c,Flush
                ret


;Get one byte from the serial port if available.
;Returns with byte in A reg with carry flag set, if carry flag clear means no character available
get_chr:        or      a           ;clear carry flag
                in      a,(3)       ;get status
                and     002h        ;check RxRDY bit
                ret     z           ;not ready, quit
                in      a,(2)       ;get char
                scf                 ;set carry flag we got a char
                ret

ptr:    dfw 0
blkno:  dfb 0
err:    dfb "Up Err!",CR,LF,0
good:   dfb "Up Ok!",CR,LF,0
msg:    dfb CR,LF,"X/CSUM <Esc> to q",CR,LF,0

Categories
z80

z80 dram

Been researching how to use DRAM in a homebuilt computer. For interests sake of course, since SRAM is so readily accessable, and much easier to use. DRAM is challenging, considering building a Z80 to try it.

Useful links:
http://www.cpm.z80.de/download/dram.pdf
of course the whole site: http://www.cpm.z80.de/

Categories
z80

cpm z80 output

Up until now I was using raw serial output for console, but I think this is not the correct way.

write_char:
        in a,(3)
        and 001h
        jp z, write_char
        ld a,b
        out (2),a
        ret

now this works, but it does not work for cpm emulator yaze-ag. I suspect this is because I should be using the syscall to output to the console.

write_char:             push de ; preserve register values
                        push bc
                        ld c,2  ; function 2
                        ld e,a  ; char to write
                        call 5  ; call bios / syscall
                        pop bc  ; restore register values
                        pop de
                        ret
Categories
z80

Conway’s Life on z80

I have not coded Conway’s life in many years – I thought it would be fun to try on the Z80. First I implemented it in C on the Raspberry Pi. It uses an array to both keep track of prior generations (to determine if the pattern is still evolving) and the same array to figure out the neighbour count.

#include <time.h>
#include <stdlib.h>
#include <stdio.h>

unsigned char *grid;
const int rows=48, cols=160, maxt=3;
int same;

void* calc_grid(int *t) {
int i, j, q;
    for (i=rows+rows-*t*rows/maxt-1; i>=rows+rows-(*t+1)*rows/maxt; i--) {
        for (j=cols+cols-1; j>=cols; j--) {

            if ( (grid[(i-rows)*cols+j-cols] & 64) ) { /* for each cell that is alive, add 1 to all the neighbours in every (8) adjacent cell */
                grid[((i-1) % rows)*cols + (j-1) % cols] += 1;
                grid[((i-1) % rows)*cols + (j  ) % cols] += 1;
                grid[((i-1) % rows)*cols + (j+1) % cols] += 1;
                grid[((i  ) % rows)*cols + (j-1) % cols] += 1;
                grid[((i  ) % rows)*cols + (j+1) % cols] += 1;
                grid[((i+1) % rows)*cols + (j-1) % cols] += 1;
                grid[((i+1) % rows)*cols + (j  ) % cols] += 1;
                grid[((i+1) % rows)*cols + (j+1) % cols] += 1;
            }
        }
    }

}
void* shift_grid(int *t) {
        int i, j;
        for (i=*t*rows/maxt; i<(*t+1)*rows/maxt; i++) {
            for (j=0; j<cols; j++) {  /* for every cell take the 7th bit and the lower 4 bits (total) - empty cell =3  - live cell = 64+2 or 64+3 - then mark the 8th bit as live for next gen*/
                if ((grid[i*cols+j] & 79) == 3 || (grid[i*cols+j] & 79) == 66 || (grid[i*cols+j] & 79) == 67)   grid[i*cols+j] |= 128;
                grid[i*cols+j] = (grid[i*cols+j] >> 1) & 240;   /* shift to the right one bit, and keep the top 4 bits 128+64+32+16 to see what was going on in prior gens  */
                if ( same == 1 && ((grid[i*cols+j] & 64)>>2) != (grid[i*cols+j] & 16) )  /* compare the 7th bit to the 5th bit- if those are not the same, then the pattern is changing */
                    same=0;
            }
        }
}

int main() {

    time_t qt;
    srand((unsigned) time(&qt));

    pthread_t thr[maxt];

    int c, x, y, i, j, gen, t, rc;

    grid = malloc(rows*cols*sizeof(char));

for ( c=0; c<1000; c++) {
//    printf("Try: %d\n", c);
    for (i=0; i<rows; i++)
        for (j=0; j<cols; j++)
            grid[i*cols+j] = 64 * (rand() % 2);  /*  initialize the array with random numbers in the 7th bit */

    for (gen=0; gen<20000; gen++) {
        for (t=0; t<maxt; t++) {
                calc_grid(&t);
        }

        same=1;
        for (t=0; t<maxt; t++) {
                shift_grid(&t);
        }
        //if (same==1) break;
        printf("Gen: %d\n", gen );
    for (i=0; i<rows; i++) {
        for (j=0; j<cols; j++)
         putc( (grid[i*cols+j] & 64 ) ? '*': ' ', stdout);
        putc('\n', stdout);
        }
        getc(stdin);
    }
    }
/*  print out survivor */
        printf("Gen: %d\n", gen );
    for (i=0; i<rows; i++) {
        for (j=0; j<cols; j++)
         putc( (grid[i*cols+j] & 64 ) ? '*': ' ', stdout);
        putc('\n', stdout);
        }

    free(grid);
}

I’ve made it very similar on the z80 in terms of the algorithm that is implemented. The grid it uses is smaller.

        org     0100h

        ; to do a given cell, we need to do IX-1, IX+1, IX-81, IX-80, IX-79, IX+79, IX+80, IX+81
        ; board is 78x24 with an extra space all around so 80x26  - 2080 elements
        ld a,0ffh    ; do at most 255 generations
top:    push af
        ld hl,c_home
        call my_write_string
        call print_board
        call calc_n
        call calc_g
        pop af
        dec a
        jr nz,top
        ld hl,c_home
        call my_write_string
        call print_board

        ret
calc_g:         ; calculate the next generation
        ld de, board
calc_g_loop:
        ld b, 080h
        ld a,(de)
        ld c,a
        and b
        jr z,no_128
        ld (de),a
        jr end_calc_g

no_128: ; continue with calcs
        ld a,c
        ld b,79
        and b
        ld b,3
        cp b
        jr z, set_128
        ld b,66
        cp b
        jr z, set_128
        ld b,67
        cp b
        jr z, set_128
        ld a,c
        jr shift_mask
set_128:
        ld a,c
        ld b,128
        or b
shift_mask:
        ld b,224
        and b
        srl a
        ld (de), a
end_calc_g:
        inc de
        ld hl,b_done
        sbc hl,de
        ret z
        jr calc_g_loop

calc_n:
        ld ix, board
        ld de, board
        ld c,040h
        ld b,080h
calc_n_loop:
        ld a,(ix+0)
        and b
        jr nz,calc_x    ; found a 128 - skip calc
        ld a,(ix+0)
        and c
        jr z,calc_x     ; only increment if its currently alive
        inc (ix-1)
        inc (ix+1)
        inc (ix-79)
        inc (ix-80)
        inc (ix-81)
        inc (ix+79)
        inc (ix+80)
        inc (ix+81)
calc_x: inc ix
        inc de
        ld hl,b_done
        sbc hl,de
        ret z
        jr calc_n_loop

        ; print_board:  routine to print out board - uses a, bc, de, hl
print_board:
        ld de, board
print_nl:
        ld c, 00h
print_loop:
        ld a,(de)
        ld b,040h
        and b
        jr z,space_char
        ld b,'@'
        jr end_char
space_char:
        ld b,' '
end_char:
        call my_write_char
        inc de
        inc c
        ld a,80
        sub c
        jr nz,print_loop
        ld hl,crlf
        call my_write_string
        ld hl,b_done
        sbc hl,de
        ret z
        jr print_nl
        ; puts a single char (byte value) on serial output
        ; call with char to send in B register; also uses A register
my_write_char:
        in a,(3)
        and 001h
        jp z, my_write_char
        ld a,b
        out (2),a
        ret
my_write_string:
        in a,(3)
        and 001h
        jr z,my_write_string
        ld a,(hl)
        and a
        ret z
        out (2),a
        inc hl
        jr my_write_string

crlf:   defm    0dh,0ah,0h
c_home: defm    01bh,"[H",0h
b_junk: defm    128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128
        defm    128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128
board:  defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,00,64,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,64,64,00,00,00,00,00,00,64,64,00,00,00,00,00,00,00,00,00,00,00,00,64,64,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,64,64,00,00,00,64,64,64,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,64,00,00,00,00,64,64,00,00,00,00,00,00,00,00,00,00,00,00,64,64,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,64,64,00,00,00,00,00,00,00,00,64,00,00,00,00,00,64,00,00,00,64,64,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,00,64,00,64,00,00,00,00,64,00,00,00,00,00,00,00,00,00,00,128
        defm    128,64,64,00,00,00,00,00,00,00,00,64,00,00,00,64,00,64,64,00,00,00,00,64,00,64,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,00,64,00,64,00,00,00,00,64,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,64,00,00,00,00,00,64,00,00,00,00,00,00,00,64,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,00,64,00,64,00,00,00,00,64,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,64,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,64,64,00,00,00,64,64,64,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,64,64,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,64,64
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,64,64,00,00,00,64,64,64,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,64,64,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,00,64,00,64,00,00,00,00,64,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,00,64,00,64,00,00,00,00,64,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,00,64,00,64,00,00,00,00,64,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,64,64,00,00,00,64,64,64,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,64,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
        defm    128,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
        defm    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,128
b_done: defm    128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128
        defm    128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128
        end
From the R-pentomino, the most common methuselah.

Categories
z80

Sieve of Eratosthenes z80

I’ve been trying to learn z80 assembler, and one of the programs that I typically do for that is writing a version of the Sieve of Eratosthenes. My version runs under CP/M on my CPUVILLE z80 computer.

; this program implements the sieve of eratosthenes on the z80
monitor:        equ     046fh
cpm:            equ     0fa00h
data_start:     equ     1000h
data_end:       equ     3000h
current_location:       equ     0x3000          ;word variable in RAM
line_count:             equ     0x3002          ;byte variable in RAM
byte_count:             equ     0x3003          ;byte variable in RAM
value_pointer:          equ     0x3004          ;word variable in RAM
current_value:          equ     0x3006          ;word variable in RAM
buffer:                 equ     0x3008          ;buffer in RAM -- up to stack area
var_c:  equ     0ffeh

org 0100h  ; set origin of execution
        ld de,data_start
        ld a,00h
zero_loop:
        ld (de),a
        inc de
        ld hl,data_end
        sbc hl,de
        jp nz,zero_loop

        ld de,02        ; start sieve at c=2
main_sieve_loop:        ; we are using de for this loop
        ld (var_c),de   ; store in ram too
        ld h,d          ; ld hl,de
        ld l,e          ; use hl to figure out memory location
        srl h           ; since we can store 8 bits in each memory loc
        rr l            ; we need to shift left (16 bit) 3 times
        srl h           ; https://chilliant.com/z80shift.html
        rr l            ; 2
        srl h
        rr l            ; 3
        ld a,10h
        add a,h         ; add 1000h to hl to get actual location
        ld h,a          ; here is the data in (hl)
        ld a,00000111b
        and e           ; last 3 bits of de into a - will need srl a times
        ld b,(hl)       ; load the data into b
        cp 0b           ; compare a to 0
shift_a_times_1:
        jp z,done_shift_1
        srl b
        dec a
        jp shift_a_times_1
done_shift_1:           ; a is garbage, b has the bit in the lsb position
        ld a,00000001b
        and b           ; Z flag will be correct here if zero
        ld (var_c),de   ; store de in memory
        jp nz,end_main_sieve_loop       ; non zero - just inc loop and get out
; we found a zero, now need to loop and mark composites
        ld h,d
        ld l,e          ; ld hl,de
        ld b,d
        ld c,e
start_composite_loop:   ; de stores the value in this loop
        add hl,bc       ; start at 2 * de to mark composites
        jp c,end_main_sieve_loop        ; get out if overflow 16 bit
        ld d,h
        ld e,l          ; ld de,hl
        srl h
        rr l
        srl h
        rr l
        srl h
        rr l
        ld a,10h
        add a,h
        ld h,a          ; here is the data in (hl)
        ld a,00000111b
        and e           ; a has which bit to set
        ld b,00000001b  ; start with lsb
        cp 0b           ; is a zero?
shift_a_times_2:
        jp z,done_shift_2
        sla b
        dec a
        jp shift_a_times_2
done_shift_2:
        ld a,(hl)       ; retreive what is in memory
        or b            ; mark the bit
        ld (hl),a       ; write back to memory
        ld h,d
        ld l,e          ; put hl back to where we were
        ld bc,(var_c)   ; put step into bc
        jp start_composite_loop
end_main_sieve_loop:
        ld de,(var_c)
        inc de
        jp nz,main_sieve_loop

        ld hl,data_start
        call memory_dump
        ld hl,2f00h
        call memory_dump

        ret
Categories
z80

Reset circuit for cpuville sbc

Some time back I built a CPUVILLE Z80 single board computer. I used a USB serial cable to connect it to one of my Raspberry Pi computers. The issue that I faced is that my assembler skills are pretty sus at this point, and I would need to go to the computer to hit the reset switch. I made a simple circuit to allow using a GPIO pin on the Pi to reset the computer.

Now I just set an alias to toggle the GPIO pin

alias z80reset='gpio -1 mode 13 out; gpio -1 write 13 0; sleep 0.1; gpio -1 write 13 1'