; Various functions: init, (RAM) disk, mmc, timer ; This file needs to get split up. ; ; Copyright (C) 2010 Sprite_tm ; Copyright (C) 2010 Leo C. ; ; This file is part of avrcpm. ; ; avrcpm is free software: you can redistribute it and/or modify it ; under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; ; avrcpm is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with avrcpm. If not, see . ; ; $Id$ ; ; ------------------- DRAM Refresh Interrupt -------------------- .cseg ; refresh interupt; exec 2 cbr cycles refrint: ;4 .org OC2Aaddr rjmp refrint ; tim2cmpa .org refrint sbis P_RAS,ram_ras ;2 reti ; CAS RAS cbi P_CAS,ram_cas ;2 1| 1| ; 1| 1| cbi P_RAS,ram_ras ;2 |0 1| ; |0 1| nop ;1 |0 |0 ; nop ;1 |0 |0 sbi P_RAS,ram_ras ;2 |0 |0 ; |0 |0 dram_wait DRAM_WAITSTATES-1 ; | | ; nop ;1 |0 |0 cbi P_RAS,ram_ras ;2 |0 1| ; |0 1| sbi P_CAS,ram_cas ;2 |0 |0 ; |0 |0 sbi P_RAS,ram_ras ;2 1| |0 ; 1| 1| reti ;4 --> 21 cycles ;Print a unsigned lonng value to the uart ; temp4:temp3:temp2:temp = value print_ultoa: push yh push yl push z_flags push temp4 push temp3 push temp2 push temp clr yl ;yl = stack level ultoa1: ldi z_flags, 32 ;yh = temp4:temp % 10 clr yh ;temp4:temp /= 10 ultoa2: lsl temp rol temp2 rol temp3 rol temp4 rol yh cpi yh,10 brcs ultoa3 subi yh,10 inc temp ultoa3: dec z_flags brne ultoa2 cpi yh, 10 ;yh is a numeral digit '0'-'9' subi yh, -'0' push yh ;Stack it inc yl cp temp,_0 ;Repeat until temp4:temp gets zero cpc temp2,_0 cpc temp3,_0 cpc temp4,_0 brne ultoa1 ldi temp, '0' ultoa5: cpi yl,3 ; at least 3 digits (ms) brge ultoa6 push temp inc yl rjmp ultoa5 ultoa6: pop temp ;Flush stacked digits rcall uartputc dec yl brne ultoa6 pop temp pop temp2 pop temp3 pop temp4 pop z_flags pop yl pop yh ret ;Prints temp2:temp in hex to the uart printhexw: push temp mov temp,temp2 rcall printhex pop temp ;fall thru ;Prints temp in hex to the uart printhex: swap temp rcall printhexn swap temp ;fall thru ;Prints the lower nibble printhexn: push temp andi temp,0xf cpi temp,0xA brlo printhexn_isno subi temp,-7 printhexn_isno: subi temp,-'0' rcall uartputc pop temp ret ;Prints the zero-terminated string following the call statement. printstr: push zh push zl push yh push yl push temp in yh,sph in yl,spl ldd zl,y+7 ldd zh,y+6 lsl zl rol zh printstr_loop: lpm temp,z+ cpi temp,0 breq printstr_end rcall uartputc cpi temp,13 brne printstr_loop ldi temp,10 rcall uartputc rjmp printstr_loop printstr_end: adiw zl,1 ;rounding lsr zh ror zl std y+7,zl std y+6,zh pop temp pop yl pop yh pop zl pop zh ret ; ---------------- Virtual peripherial interface ---------------- ;The hw is modelled to make writing a CPM BIOS easier. ;Ports: ;0 - Con status. Returns 0xFF if the UART has a byte, 0 otherwise. ;1 - Console input, aka UDR. ;2 - Console output ;3 - "UART" status: bit 0=rx, bit 1 = tx ;4 - "UART" data register, no wait ;15 - Disk select ;16,17 - Track select ;18 - Sector select ;20 - Write addr l ;21 - Write addr h ;22 - Trigger - write to read, to write a sector using the above info; ; , write to allocated/dirctory/unallocated .equ READ_FUNC = 7 .equ WRITE_FUNC = 6 .equ BOOT_FUNC = 5 .equ HOME_FUNC = 4 ;***************************************************** ;* CP/M to host disk constants * ;***************************************************** .equ MAXDISKS = 4 ;Max number of Disks (partitions) .equ blksize = 1024 ;CP/M allocation size .equ hostsize = 512 ;host disk sector size ; .equ hostspt = 20 ;host disk sectors/trk .equ hostblk = hostsize/128 ;CP/M sects/host buff ; .equ CPMSPT = hostblk*hostspt;CP/M sectors/track .equ CPMSPT = 26 ; .equ SECMSK = hostblk-1 ;sector mask .equ SECSHF = log2(hostblk) ;sector shift ;***************************************************** ;* BDOS constants on entry to write * ;***************************************************** .equ WRALL = 0 ;write to allocated .equ WRDIR = 1 ;write to directory .equ WRUAL = 2 ;write to unallocated .equ WRTMSK= 3 ;write type mask .dseg ndisks: .byte 1 ;Number of CP/M disks seekdsk: .byte 1 ;seek disk number seektrk: .byte 2 ;seek track number seeksec: .byte 1 ;seek sector number hostparttbl: .byte 8*MAXDISKS ;host partition table (start sector, sector count) hostparttbltop: hostdsk: .byte 1 ;host disk number hostlba: .byte 3 ;host sector number (relative to partition start) unacnt: .byte 1 ;unalloc rec cnt unadsk: .byte 1 ;last unalloc disk unatrk: .byte 2 ;last unalloc track unasec: .byte 1 ;last unalloc sector erflag: .byte 1 ;error reporting wrtype: .byte 1 ;write operation type dmaadr: .byte 2 ;last dma address hostbuf: .byte hostsize ;host buffer (from/to SD-card) .cseg conStatus: lds temp,rxcount cpse temp,_0 ldi temp,0xff ret conInp: rjmp uartgetc dbgOut: printnewline printstring "Debug: " rcall printhex ret conOut: rjmp uartputc uartstat: clr temp lds temp2,rxcount cpse temp2,_0 sbr temp,0x01 lds temp2,txcount cpi temp2,TXBUFSIZE breq uartst_1 sbr temp,0x02 uartst_1: ret uartout: lds temp2,txcount cpi temp2,TXBUFSIZE breq uartout_1 rjmp uartputc uartout_1: ret uartin: clr temp lds temp2,rxcount cpse temp2,_0 rjmp uartgetc ret ;Called with port in temp2. Should return value in temp. portRead: cpi temp2,0 breq conStatus cpi temp2,1 breq conInp cpi temp2,3 breq uartstat cpi temp2,4 breq uartin cpi temp2,15 breq dskDiskCheck cpi temp2,22 breq dskErrorRet cpi temp2,TIMER_MSECS brlo pr_noclock cpi temp2,TIMER_MSECS+6 brsh pr_noclock rjmp clockget pr_noclock: ldi temp,0xFF ret ;Called with port in temp2 and value in temp. portWrite: cpi temp2,0 breq dbgOut cpi temp2,2 breq conOut cpi temp2,4 breq uartout cpi temp2,15 breq dskDiskSel cpi temp2,16 breq dskTrackSel_l cpi temp2,17 breq dskTrackSel_h cpi temp2,18 breq dskSecSel cpi temp2,20 breq dskDmaL cpi temp2,21 breq dskDmaH cpi temp2,22 breq dskDoIt cpi temp2,TIMERPORT brlo pw_noclock cpi temp2,TIMER_MSECS+6 brsh pw_noclock rjmp clockput pw_noclock: ret dskDiskCheck: lds temp2,seekdsk cpi temp2,RAMDISKNR brsh dsk_dchrd ;maybe ramdisk ; Check if selected disk # is less then # of disks. lds temp,ndisks tst temp brne dsk_dchpart1 ; Need to init rcall dsk_partinit cbr temp,0x80 lds temp2,seekdsk dsk_dchpart1: cp temp2,temp brsh dsk_dcher dsk_dchend: ldi temp,0 ret dsk_dchrd: #if RAMDISKCNT cpi temp,RAMDISKNR+RAMDISKCNT brlo dsk_dchend #endif dsk_dcher: ldi temp,0xff ;error return ret dskErrorRet: lds temp,erflag ret dskDiskSel: sts seekdsk,temp ret dskTrackSel_l: sts seektrk,temp sts seektrk+1,_0 ret dskTrackSel_h: sts seektrk+1,temp ret dskSecSel: sts seeksec,temp ret dskDmal: sts dmaadr,temp ret dskDmah: sts dmaadr+1,temp ret dskDoIt: .if DISK_DEBUG push temp sbrc temp,READ_FUNC rjmp dskdbgr sbrc temp,WRITE_FUNC rjmp dskdbgw rjmp dskdbge dskdbgr: printnewline printstring "Disk read: " rjmp dskdbg1 dskdbgw: printnewline printstring "Disk write: " dskdbg1: lds temp,seekdsk subi temp,-('A') rcall uartputc printstring ": track " lds temp2,seektrk+1 lds temp,seektrk rcall printhexw printstring ", sector " lds temp,seeksec rcall printhex printstring ", dma-addr " lds temp2,dmaadr+1 lds temp,dmaadr rcall printhexw pop temp push temp sbrs temp,WRITE_FUNC rjmp dskdbge printstring " wrtype " andi temp,3 rcall printhex dskdbge: pop temp .endif ;See what has to be done. sbrc temp,READ_FUNC rjmp dsk_read sbrc temp,WRITE_FUNC rjmp dsk_write sbrc temp,HOME_FUNC rjmp dsk_home sbrc temp,BOOT_FUNC rjmp dsk_boot printstring "DISK I/O: Invalid Function code: " rcall printhex rjmp haltinv dsk_boot: sts ndisks,_0 ;no active partitions dsk_cboot: cbi flags,hostact ;host buffer inactive sts unacnt,_0 ;clear unalloc count ret dsk_home: sbis flags,hostwrt ;check for pending write cbi flags,hostact ;clear host active flag ret dsk_read: sbi flags,readop ;read operation ;RAM disk? lds temp2,seekdsk #if RAMDISKCNT cpi temp2,RAMDISKNR brlt PC+2 rjmp rdskDoIt #endif sts unacnt,_0 sbi flags,rsflag ;must read data ldi temp,WRUAL ;write type sts wrtype,temp ;treat as unalloc rjmp dsk_rwoper ;to perform the read dsk_write: ;write the selected CP/M sector cbi flags,readop ;not a read operation ;RAM disk? lds temp2,seekdsk #if RAMDISKCNT cpi temp2,RAMDISKNR brlt PC+2 rjmp rdskDoIt #endif andi temp,WRTMSK sts wrtype,temp ;save write type cpi temp,WRUAL ;write unallocated? brne dsk_chkuna ;check for unalloc ; write to unallocated, set parameters ldi temp,blksize/128 ;next unalloc recs sts unacnt,temp lds temp,seekdsk ;disk to seek sts unadsk,temp ;unadsk = sekdsk lds temp,seektrk sts unatrk,temp ;unatrk = sectrk lds temp,seektrk+1 sts unatrk+1,temp ;unatrk = sectrk lds temp,seeksec sts unasec,temp ;unasec = seksec ; dsk_chkuna: ;check for write to unallocated sector lds temp,unacnt ;any unalloc remain? tst temp breq dsk_alloc ;skip if not ; more unallocated records remain dec temp ;unacnt = unacnt-1 sts unacnt,temp lds temp,seekdsk ;same disk? lds temp2,unadsk cp temp,temp2 ;seekdsk = unadsk? brne dsk_alloc ;skip if not ; disks are the same lds temp,unatrk lds temp2,unatrk+1 lds temp3,seektrk lds temp4,seektrk+1 cp temp,temp3 ;seektrk = unatrk? cpc temp2,temp4 brne dsk_alloc ;skip if not ; tracks are the same lds temp,seeksec ;same sector? lds temp2,unasec cp temp,temp2 ;seeksec = unasec? brne dsk_alloc ;skip if not ; match, move to next sector for future ref inc temp2 ;unasec = unasec+1 sts unasec,temp2 cpi temp2,CPMSPT ;end of track? (count CP/M sectors) brlo dsk_noovf ;skip if no overflow ; overflow to next track sts unasec,_0 ;unasec = 0 lds temp,unatrk lds temp2,unatrk+1 subi temp, low(-1) ;unatrk = unatrk+1 sbci temp2,high(-1) sts unatrk,temp sts unatrk+1,temp2 ; dsk_noovf: cbi flags,rsflag ;rsflag = 0 rjmp dsk_rwoper ;to perform the write ; dsk_alloc: ;not an unallocated record, requires pre-read sts unacnt,_0 ;unacnt = 0 sbi flags,rsflag ;rsflag = 1 ;***************************************************** ;* Common code for READ and WRITE follows * ;***************************************************** dsk_rwoper: ;enter here to perform the read/write .if DISK_DEBUG printstring ", flags: " in temp,flags rcall printhex .endif sts erflag,_0 ;no errors (yet) ;Convert track/sector to an LBA address (in 128byte blocks) lds xl,seeksec ; ldi xh,0 ; ldi yl,0 ; lds temp3,seektrk ; lds temp4,seektrk+1 ; ldi temp,CPMSPT ; mul temp3,temp ; add xl,r0 ; adc xh,r1 ; mul temp4,temp ; add xh,r0 ;yl:xh:xl := sec + trk * SectorsPerTrack adc yl,r1 ; mov temp,xl andi temp,SECMSK ;mask buffer number push temp ;save for later ;Convert from CP/M LBA blocks to host LBA blocks ldi temp,SECSHF dsk_sh1: lsr yl ror xh ror xl dec temp brne dsk_sh1 ;yl:xh:xl = host block to seek ; active host sector? in _tmp0,flags ;host active flag sbi flags,hostact ;always becomes 1 sbrs _tmp0,hostact ;was it already? rjmp dsk_filhst ;fill host if not ; host buffer active, same as seek buffer? lds temp,seekdsk lds temp2,hostdsk ;same disk? cp temp,temp2 ;seekdsk = hostdsk? brne dsk_nomatch ; same disk, same block? lds temp,hostlba lds temp2,hostlba+1 lds temp3,hostlba+2 cp xl,temp cpc xh,temp2 cpc yl,temp3 breq dsk_match ; dsk_nomatch: ;proper disk, but not correct sector sbis flags,hostwrt ;host written? rjmp dsk_filhst push xl push xh push yl rcall dsk_writehost ;clear host buff pop yl pop xh pop xl dsk_filhst: ;may have to fill the host buffer lds temp,seekdsk sts hostdsk,temp sts hostlba,xl sts hostlba+1,xh sts hostlba+2,yl sbic flags,rsflag ;need to read? rcall dsk_readhost ;yes, if 1 cbi flags,hostwrt ;no pending write dsk_match: ;copy data to or from buffer ldiw z,hostbuf ldi temp,128 pop temp2 ;get buffer number (which part of hostbuf) mul temp2,temp add zl,r0 ;offset in hostbuf adc zh,r1 .if DISK_DEBUG > 2 push r0 push r1 printstring "; host buf adr: " pop temp2 pop temp rcall printhexw .endif lds xl,dmaadr lds xh,dmaadr+1 ldi temp3,128 ;length of move sbic flags,readop ;which way? rjmp dsk_rmove ;skip if read ; mark write operation sbi flags,hostwrt ;hostwrt = 1 dsk_wmove: mem_read st z+,temp adiw xl,1 dec temp3 brne dsk_wmove rjmp dsk_rwmfin dsk_rmove: ld temp,z+ mem_write adiw xl,1 dec temp3 brne dsk_rmove dsk_rwmfin: ; data has been moved to/from host buffer lds temp,wrtype ;write type cpi temp,WRDIR ;to directory? breq dsk_wdir ret ;no further processing dsk_wdir: ; clear host buffer for directory write lds temp,erflag tst temp ;errors? breq dsk_wdir1 ret ;skip if so dsk_wdir1: rcall dsk_writehost ;clear host buff cbi flags,hostwrt ;buffer written ret ;***************************************************** ; hostdsk = host disk #, (partition #) ; hostlba = host block #, relative to partition start ; Read/Write "hostsize" bytes to/from hostbuf dsk_hostparam: ldiw z,hostparttbl lds temp,hostdsk .if HOSTRW_DEBUG push temp subi temp,-('A') rcall uartputc printstring ": " pop temp .endif lsl temp lsl temp lsl temp add zl,temp adc zh,_0 lds temp,hostlba lds temp2,hostlba+1 lds temp3,hostlba+2 .if HOSTRW_DEBUG printstring "lba: " clr temp4 rcall print_ultoa .endif ldd xl,z+4 ldd xh,z+5 ldd yl,z+6 cp temp,xl cpc temp2,xh cpc temp3,yl brcs dsk_hp1 .if HOSTRW_DEBUG printstring ", max: " push temp4 push temp3 push temp2 push temp movw temp,x mov temp3,yl clr temp4 rcall print_ultoa pop temp pop temp2 pop temp3 pop temp4 printstring " " .endif clr temp ret dsk_hp1: ldd xl,z+0 ldd xh,z+1 ldd yl,z+2 ldd yh,z+3 add xl,temp adc xh,temp2 adc yl,temp3 adc yh,_0 .if HOSTRW_DEBUG printstring ", abs:" push temp4 push temp3 push temp2 push temp movw temp,x movw temp3,y rcall print_ultoa pop temp pop temp2 pop temp3 pop temp4 printstring " " .endif ori temp,255 dsk_hpex: ret ;***************************************************** ;* WRITEhost performs the physical write to * ;* the host disk, READhost reads the physical * ;* disk. * ;***************************************************** dsk_writehost: .if HOSTRW_DEBUG printnewline printstring "host write " .endif rcall dsk_hostparam breq dsk_rdwr_err rcall mmcWriteSect tst temp breq dsk_rdwr_ok rcall dsk_partinit cbr temp,0x80 breq dsk_rdwr_err rcall dsk_hostparam breq dsk_rdwr_err rcall mmcWriteSect tst temp brne dsk_rdwr_err rjmp dsk_rdwr_ok dsk_readhost: .if HOSTRW_DEBUG printnewline printstring "host read " .endif rcall dsk_hostparam breq dsk_rdwr_err rcall mmcReadSect tst temp breq dsk_rdwr_ok rcall dsk_partinit cbr temp,0x80 breq dsk_rdwr_err rcall dsk_hostparam breq dsk_rdwr_err rcall mmcReadSect tst temp brne dsk_rdwr_err dsk_rdwr_ok: sts erflag,_0 ret dsk_rdwr_err: sts erflag,_255 ret ;*************************************************************************** #if RAMDISKCNT ; ----------------- RAM disk ----------------- .dseg rdskbuf: .byte 128 .cseg ;---------------------------------------------- rdsk_adr: ldi xl,0 lds xh,seeksec lds temp2,seektrk lsr xh ror xl ;Col 0..7 mov temp,temp2 andi temp,0x0f swap temp or xh,temp ;Row 0..7 ldiw z,rdskbuf ldi temp3,128 DRAM_SETADDR xh, ~0,(1< 1 mov temp,xh rcall printhex printstring " " mov temp,xl rcall printhex printstring " " .endif ret ;---------------------------------------------- rdskDoIt: sts erflag,_0 sbis flags,readop rjmp rdsk_wr .if DISK_DEBUG > 1 printnewline printstring "rd-adr: " .endif rcall rdsk_adr rdsk_rdl: DRAM_SETADDR xl, ~(1< 1 printnewline printstring "wr-adr: " .endif lds xl,dmaadr lds xh,dmaadr+1 ldiw z,rdskbuf ldi temp3,128 rdsk_wrldl: mem_read st z+,temp adiw x,1 dec temp3 brne rdsk_wrldl ldi temp2,RAM_DQ_MASK | (1<