📄 test_dram.asm
字号:
mov dptr, #buffer + 6 acall print_parm mov dptr, #msg_sc lcall pstr mov dptr, #buffer + 12 acall print_parm lcall newline lcall newline mov dptr, #buffer + 6 movx a, @dptr dec a mov lba+0, a mov dptr, #buffer + 12 movx a, @dptr mov lba+1, a lcall init_parms ; default position will be first block (master boot record) clr a mov lba+0, a mov lba+1, a mov lba+2, a mov lba+3, amain_loop: ;print a 1-line prompt mov dptr, #msg_l lcall pstr mov a, lba+3 lcall phex mov a, lba+2 lcall phex mov a, lba+1 lcall phex mov a, lba+0 lcall phex mov dptr, #msg_pmt lcall pstr lcall cin lcall uppermain1: cjne a, #'R', main2 ;read a sector acall read_sector jz main1b push acc mov dptr, #msg_err lcall pstr pop acc lcall phex lcall newline ajmp main_loopmain1b: mov dptr, #msg_rd lcall pstr ajmp main_loopmain2: cjne a, #'W', main3 ;write a sector mov dptr, #msg_sure lcall pstr lcall cin lcall upper cjne a, #'Y', main2c acall write_sector jz main2b push acc mov dptr, #msg_err lcall pstr pop acc lcall phex lcall newline ajmp main_loopmain2b: mov dptr, #msg_wr lcall pstrmain2c: ajmp main_loopmain3: cjne a, #'L', main4 ;set the logical block address mov dptr, #msg_cyh lcall pstr lcall ghex32_lba jc main3b mov lba+0, r3 mov lba+1, r4 mov lba+2, r5 mov lba+3, r6main3b: lcall newline ajmp main_loopmain4: cjne a, #'U', main5 ;cause the drive to spin up acall spinup ajmp main_loopmain5: cjne a, #'D', main6 ;cause the drive to spin down acall spindown ajmp main_loopmain6: cjne a, #'Q', main7 ;quit ljmp 0main7: cjne a, #'H', main8 acall hexdump ajmp main_loopmain8: ajmp main_loop;------------------------------------------------------------------; Routines that talk with the IDE drive, these should be called by; the main program. ;read a sector, specified by the 4 bytes in "lba", ;Return, acc is zero on success, non-zero for an errorread_sector: acall ide_busy ;make sure drive is ready acall wr_lba ;tell it which sector we want mov dptr, #ide_command mov a, #ide_cmd_read movx @dptr, a ;ask the drive to read it acall ide_drq ;wait until it's got the data jb acc.0, get_err mov dptr, #buffer acall read_data ;grab the data clr a ret ;when an error occurs, we get acc.0 set from a call to ide_drq ;or ide_busy (which read the drive's status register). If ;that error bit is set, we should jump here to read the drive's ;explaination of the error, to be returned to the user. If for ;some reason the error code is zero (shouldn't happen), we'll ;return 255, so that the main program can always depend on a ;return of zero to indicate success.get_err:mov dptr, #ide_err movx a, @dptr jz gerr2 retgerr2: mov a, #255 ret ;write a sector, specified by the 4 bytes in "lba", ;whatever is in the buffer gets written to the drive! ;Return, acc is zero on success, non-zero for an errorwrite_sector: acall ide_busy ;make sure drive is ready acall wr_lba ;tell it which sector we want mov dptr, #ide_command mov a, #ide_cmd_write movx @dptr, a ;tell drive to write a sector acall ide_drq ;wait unit it wants the data jb acc.0, get_err mov dptr, #buffer acall write_data ;give the data to the drive acall ide_busy ;wait until the write is complete jb acc.0, get_err clr a retinit_parms: acall ide_busy mov a, lba+0 ;get # heads for this drive anl a, #0x0F orl a, #0xA0 ;master device assumed mov dptr, #ide_head movx @dptr, a mov a, lba+1 mov dptr, #ide_sec_cnt ;get # sec/track for this drive movx @dptr, a mov a, #ide_cmd_initparms mov dptr, #ide_command movx @dptr, a acall ide_busy ret ;do the identify drive command, and return with the buffer ;filled with info about the drivedrive_id: acall ide_busy mov dptr, #ide_command mov a, #ide_cmd_id movx @dptr, a acall ide_drq mov dptr, #buffer acall read_data ret ;tell the drive to spin upspinup: mov dptr, #ide_command mov a, #ide_cmd_spinup movx @dptr, a ajmp ide_busy ;tell the drive to spin downspindown: acall ide_busy mov dptr, #ide_command mov a, #ide_cmd_spindown movx @dptr, a ajmp ide_busy ;initialize the ide driveide_init: acall ide_hard_resetide_init_wait: mov dptr, #ide_head mov a, #10100000b movx @dptr, a ;select the master device acall wait mov a, #'*' lcall cout mov dptr, #ide_status movx a, @dptr ;should probably check for a timeout here jnb acc.6, ide_init_wait ;wait for RDY bit to be set jb acc.7, ide_init_wait ;wait for BSY bit to be clear lcall newline mov dptr, #ide_command mov a, #ide_cmd_recal ;do recal command (is this necessary?) movx @dptr, a ajmp ide_busy;------------------------------------------------------------------; Not quite as low, low level I/O. These routines talk to the drive,; using the low level I/O. Normally a main program should not call; directly to these. ;Read a block of 512 bytes (one sector) from the drive ;and store it in memory @ DPTRread_data: ;ajmp read_data_pio ;read 512 bytes of data using fast DMAread_data_dma: mov a, dpl mov b, dph mov dptr, #dma_ide_dest movx @dptr, a ;write LSB of dest addr inc dptr mov a, b movx @dptr, a ;write LSB of dest addr mov dptr, #dma_ide_count clr a movx @dptr, a inc dptr mov a, #1 movx @dptr, a ;request 256 16 bit reads mov dptr, #irq_dma_ide_ack movx @dptr, a ;clear any previous interrupt mov dptr, #dma_ide_go movx @dptr, a ;begin the transfer ;now we just wait for it to finish mov dptr, #irq_identdma_wait: movx a, @dptr ;2 1 psen = 2 bytes transfered jnb acc.0, dma_wait ;2 3 psen = 6 bytes transfered ret ;speed = 8 bytes per 4 cycles -> 1229 kbyte/sec ; remember, this is raw transfer speed, and doesn't ; include the time to set up the transfer. ;Ideally, we'd be executing real code instead of this ;busy loop. With some luck, real code would have more ;psen's per second than movx (only mul and div have less). ;If we call into PM2, the psens (and dma) will stall! ;Maybe someday the DMA controller will be able to detect ;unused bus time and use it... that'd double the speed for ;this test case. Will need to investigate actual speed once ;the drivers are written and somewhat mature. A really ;ambitious approach would be to watch the psen fetch and ;do a little table lookup of the opcode, and let the state ;machine run full speed for the entire instruction and ;into the fetch of the next opcode, so we could use all ;the available time. Maybe someday when I have lots of ;free time (highly unlikely). ;read 512 bytes of data using slow I/Oread_data_pio: mov r5, #0rdblk2: push dph ;2 push dpl ;2 mov dptr, #ide_data ;2 movx a, @dptr ;2 read lsb mov r2, a ;1 mov dptr, #ide_data_buf_msb ;2 movx a, @dptr ;2 read msb mov r3, a ;1 pop dpl ;2 pop dph ;2 mov a, r2 ;1 movx @dptr, a ;2 write lsb inc dptr ;2 mov a, r3 ;1 movx @dptr, a ;2 write msb inc dptr ;2 djnz r5, rdblk2 ;2 ret ;speed = 2 bytes per 30 cycles -> 41 kbyte/sec ;Write a block of 512 bytes (at DPTR) to the drivewrite_data: mov r5, #0wrblk2: movx a, @dptr ;read lsb mov r2, a inc dptr movx a, @dptr ;read msb mov r3, a inc dptr push dph push dpl mov dptr, #ide_data_buf_msb mov a, r3 movx @dptr, a ;write msb mov dptr, #ide_data mov a, r2 movx @dptr, a ;write lsb pop dpl pop dph djnz r5, wrblk2 ret ;write the logical block address to the drive's registerswr_lba: mov a, lba+3 anl a, #0x0F orl a, #0xE0 mov dptr, #ide_head movx @dptr, a mov a, lba+2 mov dptr, #ide_cyl_msb movx @dptr, a mov a, lba+1 mov dptr, #ide_cyl_lsb movx @dptr, a mov a, lba+0 mov dptr, #ide_sector movx @dptr, a mov a, #1 mov dptr, #ide_sec_cnt movx @dptr, a ret ;Wait for the ide drive to not be busy. ;Returns the drive's status in Accide_busy: acall wait ;mov a, #'.' ;lcall cout mov dptr, #ide_status ;wait for RDY bit to be set movx a, @dptr ;should probably check for a timeout here jb acc.7, ide_busy ret ;Wait for the drive to be ready to transfer data. ;Returns the drive's status in Accide_drq: acall wait ;mov a, #'_' ;lcall cout mov dptr, #ide_status ;wait for DRQ bit to be set movx a, @dptr ;should probably check for a timeout here jb acc.7, ide_drq ;wait for BSY to be clear jnb acc.3, ide_drq ;wait for DRQ to be set ret ;do a hard reset on the drive, by pulsing its reset pin. ;this should usually be followed with a call to "ide_init".ide_hard_reset: mov dptr, #ide_rst_bit clr a movx @dptr, a ;assert the reset pin acall delay_10ms ;IDE requires reset > 25 ms acall delay_10ms acall delay_10ms mov a, #1 movx @dptr, a ;de-assert the reset pin acall delay_10ms ;brief delay while drive inits ret ;delay for 10 ms ;10ms at 7.3728 MHz is 6144 machine cycles ;6144 = 256 * 24delay_10ms: mov r0, #0d1ms: mov r1, #10 ;1 cycle djnz r1, * ;20 cycles nop ;1 cycle djnz r0, d1ms ;2 cyclesfive_cy:nop ret;------------------------------------------------------------------; Some additional serial I/O routines not available in PAULMON ;print a 16 bit number, located at DPTRprint_parm: movx a, @dptr push acc inc dptr movx a, @dptr mov dph, a pop dpl ljmp pint16u ;print a string, no more than R0 chars longprint_name: movx a, @dptr jz pn_end lcall cout inc dptr djnz r0, print_namepn_end: ret ;get a 32 bit input, in hexghex32_lba: mov r2, #8 mov r3, #0 mov r4, #0 mov r5, #0 mov r6, #0gh32c: lcall cin_filter lcall upper cjne a, #27, gh32d setb c retgh32d: cjne a, #8, gh32f sjmp gh32kgh32f: cjne a, #127, gh32ggh32k: cjne r2, #8, gh32e sjmp gh32cgh32e: lcall cout mov r0, #4gh32yy: clr c mov a, r6 rrc a mov r6, a mov a, r5 rrc a mov r5, a mov a, r4 rrc a mov r4, a mov a, r3 rrc a mov r3, a djnz r0, gh32yy inc r2 sjmp gh32cgh32g: cjne a, #13, gh32i clr c retgh32i: mov r7, a acall asc2hex jc gh32c xch a, r7 lcall cout mov a, r7 swap a mov r7, a mov r0, #4gh32j: mov a, r7 rlc a mov r7, a mov a, r3 rlc a mov r3, a mov a, r4 rlc a mov r4, a mov a, r5 rlc a mov r5, a mov a, r6 rlc a mov r6, a djnz r0, gh32j djnz r2, gh32c clr c ret ;carry set if invalid inputasc2hex: clr c add a, #208 jnc hex_not add a, #246 jc hex_maybe add a, #10 clr c rethex_maybe: add a, #249 jnc hex_not add a, #250 jc hex_not add a, #16 clr c rethex_not:setb c ret ;print a hexdump of the data in the 512 byte bufferhexdump: lcall newline mov r2, #32 ;print 32 lines mov dptr, #bufferhd1: mov a, dph mov r5, a clr c subb a, #buffer >> 8 lcall phex ;print address, starting at zero mov a, dpl mov r4, a lcall phex mov a, #':' lcall cout acall space mov r3, #16 ;print 16 hex bytes per linehd2: movx a, @dptr inc dptr lcall phex ;print each byte in hex acall space djnz r3, hd2 acall space acall space acall space mov dpl, r4 mov dph, r5 mov r3, #16 ;print 16 ascii bytes per linehd3: movx a, @dptr inc dptr anl a, #01111111b ;only 7 bits for ascii cjne a, #127, hd3b clr a ;avoid 127/255 (delete/rubout) charhd3b: add a, #224 jc hd3c clr a ;avoid control charactershd3c: add a, #32 lcall cout djnz r3, hd3 lcall newline djnz r2, hd1 lcall newline retspace: push acc mov a, #' ' lcall cout pop acc retwait: jb ri, quitwait_ret: retquit: lcall cin cjne a, #27, wait_ret ljmp 0map_block: anl a, #15 rl a add a, #dram_page_cfg & 255 mov dpl, a clr a addc a, #dram_page_cfg >> 8 mov dph, a mov a, r2 movx @dptr, a inc dptr mov a, r3 movx @dptr, a retmsg_dma_test: .db 13,10,"Testing SIMM->STA013 DMA...",13,10 .db "Config STA013: ",0msg_warn_simm: .db "ERROR: at "msg_block: .db "block=",0msg_1: .db "IDE Disk Drive Utility",13,10,13,10,0msg_mdl:.db "Model: ",0msg_sn: .db "S/N: ",0msg_cy: .db "Cylinders: ", 0msg_hd: .db ", Heads: ", 0msg_sc: .db ", Sectors: ", 0msg_cyh:.db "Enter LBA (in hex): ", 0msg_l: .db "LBA=0x",0msg_pmt:.db ", (R)ead (W)rite (L)BA (U)p (D)own (H)exdump (Q)uit",13,10,0msg_sure:.db "Warning: this will change data on the drive, are you sure?",13,10,0msg_rd: .db "Sector Read OK",13,10,0msg_wr: .db "Sector Write OK",13,10,0msg_err:.db "Error, code = ",0mesg_xilinx_cfg: .db "Config Xilinx: ",0mesg_ok: .db "Ok",13,10,0mesg_error: .db "Error!",13,10,0msg_found: .db "Found ",0msg_usable_mem: .db " megs, mask=",0msg_ck_simm_block: .db "4k block integrity: ",0msg_simm_block_err: .db "Incompatible SIMM!!",13,10,0
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -