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
electronics

8-bit IDE interface

Best articles found:

  1. http://blog.retroleum.co.uk/electronics-articles/an-8-bit-ide-interface/
  2. https://www.pjrc.com/tech/8051/ide/wesley.html
Categories
electronics

Divide by 3 and 5 circuits

Found this link has some good circuits. https://pages.mtu.edu/~suits/electronics/Divide_by_3&5_circuit.html

Copied here if page goes down:

I was interested in generating a 40 Hz clock with close to a 50% duty cycle starting a signal derived from line power. In the U.S., line power is at 60Hz, so doubling that to 120 Hz, then dividing bt three would yield 40 Hz. In much of the rest of the world line power is 50 Hz. That can be doubled twice to get 200 Hz, which can then be divided by 5 to get 40 Hz.

There are, of course, a number of ways to divide by 3 or 5. It is a little more challenging to end up with something approaching a 50% duty cycle. You could program your Arduino to do it, if nothing else. However, I took the challenge to be to do it with minimal component cost and minimal parts count.

I found several divide by three circuits on the internet, however the chip count seemed higher than necessary. After some thought, I came up with a divide by three circuit that uses two 14-pin integrated circuits that I had in my “junk box:” a 74LS74 dual flip-flop and a 74LS02 quad NOR gate. If the clock has a 50% duty cycle, the output of the divider also has a 50% duty cycle.

More generally, if the clock has a duty cycle equal to D, this circuit will output with a duty cycle of (2-D)/3, which is always closer to 50% than D. The outputs from either of the flip-flops will be at the clock frequency divided by 3, but with a 33% (or 66%) duty cycle. The extra three NORs, which are already there, are used to make the output closer to a 50% duty cycle.

For those who might need a divide by 5, I came up with one that uses three 14-pin logic ICs – two dual D flip-flops and a quad NAND. This divide by five will have a duty cycle equal to (3-D)/5, which is also always closer to 50% than is the input clock.

The circuits shown use an edge-triggered flip-flop that triggers on the rising edge (e.g., a 7474). If your flip-flops trigger on the falling edge, no changes are necessary for the divide by 5 circuit, and the divide by 3 circuit only needs to have the NOR inverter to be relocated. Oscilloscope traces are shown below (sorry, I switched the channel 1 and 2 cables, but did not notice until later).