Files
Z8C-Homebrew-Computer/OperatingSystem/monitor_v2/include/xmodem.s
Dennis Gunia bc1b9a399d Updated
2022-12-16 20:45:34 +01:00

286 lines
7.7 KiB
ArmAsm

;-------------------------------------------------------------------------
; Z80 XMODEM implementation by Dennis Gunia
; 2022 - www,dennisgunia.de
;
; important doc: https://web.mit.edu/6.115/www/amulet/xmodem.htm
; http://www.blunk-electronic.de/train-z/pdf/xymodem.pdf
;-------------------------------------------------------------------------
;Symbols
SYM_SOH equ 0x01
SYM_EOT equ 0x04
SYM_ACK equ 0x06
SYM_NAK equ 0x15
SYM_ETB equ 0x17
SYM_CAN equ 0x18
SYM_C equ 0x43
;Memory locations
MEM_VAR_BLOCK equ 0x40FB ;last block
MEM_VAR_TIMEA equ 0x40FC ;timer var (mills)
MEM_VAR_TIMER equ 0x40FE ;timer var (seconds)
MEM_INT_VEC_T equ 0x40FE ;interrupt vector table
MEM_LOC_LOAD equ 0x4400 ;load location for program
;XMODEM routine
xmodem_init:
call A_RTS_OFF
LD A,10100111b ; Init CTC Channel 3
OUT (CS_CTC_2),A
LD A,14 ; 1028.57Hz ISR
OUT (CS_CTC_2),A
LD A,00h ; Set CTC Ch3 Interrupt Vector
OUT (CS_CTC_0),A
;load int vector to ram
ld hl,xmodem_int ;CTC Ch3 INT routine
ld (0x4200 + 0x04),hl
;reset timer vars
ld hl,0x0000
ld (MEM_VAR_TIMEA),hl
ld (MEM_VAR_TIMER),hl
ld a,0x42 ; Set interrupt vector register
ld i,a
im 2 ; Z80 Interrupt mode
ld hl,MSG_START ; Print start banner
call print_str
ei ; Enable interrupts
; init done. Continue with xmodem_await_conn
call xmodem_wait
xmodem_await_conn: ;Wait for initial connection
ld a, SYM_C ;Send C to notify sender, that we want CRC
call xmodem_out
call xmodem_read_wait ;Read with timeout
jp c, xmodem_await_conn ;Carry flag set = timeout -> repeat
;else continue
xmodem_packet: ;XmodemCRC packet start
;use 1st byte to decide further processing
cp SYM_EOT ;End of Transmission
jp z, xmodem_packet_EOT
cp SYM_CAN ;Cancel (Force receiver to start sending C's)
jp z, xmodem_await_conn
cp SYM_SOH ;Start of
jp z, xmodem_packet_get
jp xmodem_err ;anything else is an error -> abort transmission
xmodem_packet_get: ;if first byte == SYM_SOH -> receive block
call xmodem_read_wait ;get byte 2 => block ID
jp c, xmodem_nak
ld b,a
ld (MEM_VAR_BLOCK), a ;store block id to memory
call xmodem_read_wait ;get byte 3 => block ID complement
jp c, xmodem_nak
add b
cp 255 ;both size infos should always sum to 255
jp nz,xmodem_err ;if not 255 then its an error
;calculate block start address in RAM
;multiply by 128
;use more efficient bit-wise operations
dec a ;a-1 to remove 1 sector offset
ld a,b
rra ;shift 1 bit to the right
and 0x7F
ld h,a
ld a,b
dec a ;a-1 to remove 1 sector offset
rrca ;shift bit0 to bit 7
and 0x80 ;mask out all other bits
ld l,a
;result:
;hl = 0aaaaaaa a000000
ld de,MEM_LOC_LOAD
add hl,de ;add calculated offset to base address
;hl now contains the true start address of this sector
ld b,128 ;preload counter for data baytes
ld c,0 ;packet length counter ( used for overflow error )
xmodem_packet_get_data: ;get 128 data bytes (loop)
push hl ;push hl onto stack because xmodem_read_wait destroys hl
call xmodem_read_wait ;read byte or timeout
jp c, xmodem_nak ;if timeout -> nak and retry
pop hl ;restore hl
ld (hl), a ;store received byte in memory
inc hl ;increment pointer
inc c ;increment packet length counter
dec b ;decerment data bytes remmaining
jp nz, xmodem_packet_get_data ;if bytes remaining, loop
;else continue with crc bytes
xmodem_packet_get_crc: ;get 16-Bit CRC
call xmodem_read_wait
jp c, xmodem_nak
ld d,a
inc c
call xmodem_read_wait
jp c, xmodem_nak
ld e,a
inc c
;de now contains CRC value
;check if c is not bigger than 130 byte (128 data + 2crc)
;TODO if so NACK
;de contains 16-bit CRC
;TODO if crc error NACK
jp xmodem_ack ;ack block -> then jump to start again
xmodem_packet_EOT: ;End of transmission SUB.
ld a, SYM_ACK ;Acknowledge EOT
call xmodem_out
jp xmodem_end ;and end xmodem
xmodem_err: ;non recoverable error -> abort
ld a, SYM_CAN
call xmodem_out
ld a, SYM_CAN
call xmodem_out
ld a, SYM_CAN
call xmodem_out
ld a, SYM_CAN
call xmodem_out
ld a, SYM_CAN
call xmodem_out
ld a, SYM_CAN
call xmodem_out
ld a, SYM_CAN
call xmodem_out
ld a, SYM_CAN
call xmodem_out
ld a, SYM_CAN
call xmodem_out
ld a, SYM_CAN
call xmodem_out
ld a, SYM_CAN
ld hl, MSG_ERROR
call print_str
;overflow to end
xmodem_end:
di ;disable interrupts
call print_newLine ;print new line
jp PROMPT_BEGIN ;return ti prompt
;isr is used as counter for timeouts
xmodem_int:
di ;setup ISR (disable further interrupts, exchange registers)
ex AF,AF'
exx
ld hl,(MEM_VAR_TIMEA) ;millis counter
inc hl
ld (MEM_VAR_TIMEA),hl
ld de,1028 ;every 1028 millis counter
sbc hl,de
jp nz, xmodem_int_cont ;if less than 1028 millis, loop
ld hl,0 ;reset millis
ld (MEM_VAR_TIMEA),hl
ld hl,(MEM_VAR_TIMER) ;incement seconds
inc hl
ld (MEM_VAR_TIMER),hl
xmodem_int_cont: ;end isr
ex AF,AF' ;restore registers
exx
EI ;enable interrupts
reti ;exit ISR
; A returns char
; Carry is set if timeout
xmodem_read_timeount equ 3 ;3 seconds timeout
xmodem_read_wait:
di
ld hl,0
ld (MEM_VAR_TIMEA),hl
ld (MEM_VAR_TIMER),hl
ei
call A_RTS_ON
xmodem_read_wait_loop:
;check timeout
ld hl,(MEM_VAR_TIMER)
ld a, l
cp xmodem_read_timeount
jp z, xmodem_read_wait_timeout ;if timeout retry
; if no timeout
xor a ; a = 0
out (CS_SIO_A_C), a ; select reg 0
in a, (CS_SIO_A_C) ; read reg 0
and 1 ; mask D0 (recieve char available)
jp Z,xmodem_read_wait_loop ; wait if no char
; if char avail
in a, (CS_SIO_A_D) ; read char
;call debug_a_hex
push af
call A_RTS_OFF
pop af
scf
ccf
ret ; return
xmodem_read_wait_timeout:
scf
ret
xmodem_out:
out (CS_SIO_A_D), a
call xmodem_wait_out
ret
xmodem_wait_out:
sub a ;clear a, write into WR0: select RR0
inc a ;select RR1
out (CS_SIO_A_C),A
in A,(CS_SIO_A_C) ;read RRx
bit 0,A
jr z,xmodem_wait_out
ret
MSG_ERROR:
db "Error: unexpected byte",13,10,0
MSG_START:
db "Await xmodem connection",13,10,0
xmodem_ack: ;ack routine. Only use when expecting transmission afterwards.
ld a, SYM_ACK ;send ACK
call xmodem_out
call xmodem_read_wait ;wait for response
jp c, xmodem_ack ;if timeout repeat
jp xmodem_packet ;if received, continue with new packet
xmodem_nak: ;nak routine. Only use when expecting transmission afterwards.
ld a, SYM_NAK ;send NAK
call xmodem_out
call xmodem_read_wait ;wait for response
jp c, xmodem_nak ;if timeout repeat
jp xmodem_packet ;if received, continue with new packet
xmodem_wait:
ld hl, 0xFF
ld bc, 0x01
xmodem_wait_1:
nop
nop
nop
nop
sbc hl,bc
ret Z
jr xmodem_wait_1