; HP IDE Interface Code with USB enhancements ; Full version w/ debug code 10/18/08 ; 10/19/08 - added equates for ports and memory usage, ; changed stream error numbers, commented debug sets. ; modify below to configure... .equ port1base, 0xF800 ;0x4000 for original, 0xF800 for Rev4/Rev5 .equ port2base, 0xF900 ;0x6000 for original, 0xF900 for Rev4/Rev5 .equ idebufbase, 0x2300 ;start of ide/usb buffer ram .equ readbufsize, 2304 ;size of usb read buffer .equ writebufsize, 2304 ;size of usb write buffer .equ usbbufbase, idebufbase + 2560 ;put usb buffers after ide buffers .equ usbvarbase, usbbufbase + readbufsize + writebufsize ;usb vars follow .equ location, 0x8000 ;location of IDE code, req's about 3.0KB .equ usblocat, 0x9000 ;location of USB code, req's about 4.6KB (sep.header) .equ autolocat, 0xF000 ;location of autobaud init program .equ baud_const, 244 ;9600 baud w/ 22.1182 MHz ; autobaud program at end, remove if Paulmon patched for fixed baud ; --------------------- ; Current status: Tested on my system w/ 0 then XXX blocks (up to a maximum of 31) is read from disk ; and then transfered to the HP. ; 111XXX - WRB - Writes XXX blocks from HP to disk. If XXX=0 the ; contents of the disk data buffer are written to the disk at the ; current block address. If XXX>0 then XXX blocks (up to a maximum ; of 31) is transfered from the host and written to disk. ; ; 10/4/08 - Added options on the debug menu to save and restore the ; IDE disk to/from USB thumbdrive using a VDRIVE2 module, and an option ; to call a SPI/USB debugging menu. See that code for comments. ; Code for defaulting to 9600 baud now included with this source. ; ; 10/6/08 - new IDE commands... ; 105xxx - request status byte with handshaking (placeholder, don't use) ; 120xxx - read one byte from VDRIVE2 (bit 15 set lsb clear if not valid) ; 121bbb - send byte bbb to the VDRIVE2 (will hang if not accepting) ; 122xxx - call VDRIVE2 sync code (will hang if not accepting) ; 123xxx - call code to clear buffered VDRIVE2 responses ; changed the control byte for port F in all hp_config sets so ; bits 4-7 remain output to avoid noise in the SPI lines. ; ; 10/12/08 - new IDE commands for streaming... ; ; 130xxx - open buffered input file - must follow by sending filename[cr] ; 131xxx - read one byte from buffered input file (if EOF or not open returns 0) ; 134xxx - open buffered output file - must follow by sending filename[cr] ; 135bbb - write byte bbb to buffered output file (if not open then no-op) ; 136xxx - close buffered output file (if not open then no-op) ; 137xxx - return usb_error value (0 means no error, not at eof) ; ; Drive connection details... ; ; PB7 O--------------------------------------------. ; PB6 O------------------------------------------. | ; PB5 O----------------------------------------. | | ; PB4 O--------------------------------------. | | | ; PB3 O------------------------------------. | | | | ; PB2 O----------------------------------. | | | | | ; PB1 O--------------------------------. | | | | | | ; PB0 O------------------------------. | | | | | | | ; 1 | | | | | | | | ; .--------|>o-------O O---. | | | | | | | | ; PA7 O---|------------------O O---|-' | | | | | | | ; PA6 O---|------------------O O---|---' | | | | | | ; PA5 O---|------------------O O---|-----' | | | | | ; PA4 O---|------------------O O---|-------' | | | | ; PA3 O---|------------------O O---|---------' | | | ; PA2 O---|------------------O O---|-----------' | | ; PA1 O---|------------------O O---|-------------' | ; PA0 O---|------------------O O---|---------------' ; | .---O x | ; | _|_ O O---* IDE Header ; PC7 O---' .----|>o-------O O---* Looking at top ; PC6 O-------|-. .----O O---* of dev board ; PC5 O-------' `--|>o--' O O | x=remove pin ; PC4 O---------. O O---*---. ; PC3 O-------. | O O | _|_ ; PC2 O-----. | | .-----O O | ; PC1 O-----|-|-|------' .---O O---|---. ; PC0 O---. | `-|--|>o---|---O O---|-. | ; `-|---|--------' .-O O---* | | ; | `--|>o-----|-------|-' | ; `--------------|-------|---' ; +5V O---*--------470-. `---. | ; _|_ | ^^ | | Inverters ; .1u -.- `--|>|--' | 74HC04 etc ; | Activity | ; GND O---*------------------------' ; ; HP Interface details... ; ; 2nd 8255 on the Paul board are labeled PD, PE and PF (for A,B,C). ; PD - I/O data bits 0-7 ; PE - I/O data bits 8-15 ; PF bit 0 - input from mode switch, high for normal, low to debug ; PF bit 1 - input from HP command line, high to trigger action ; 8052 port 1 is used to output control signals... ; bit 0 - high when executing command ; bit 1 - high when operating ; bit 2 - high when block input from HP being performed (disk write) ; bit 3 - high when block output to HP being performed (disk read) ; bit 4 - high if an error occured ; bit 5 - low to high to latch output data into register to send to HP ; bit 6 - high when reading data from HP, low when writing to HP (d_data) ; bit 7 - low to high to indicate completion, connected to HP flag input ; ; NOTE!!! P1 has very feeble drive when high (~20uA, data sheet says as low ; as 1uA, ouch), bits 5-7 should be pulled high with external 10K or so R's ; and for a 12V-compatible flag an extra emitter-follower stage may be needed. ; I wired LED's + to LED to 2.2K to P1.0 to P1.5, it looks like my LED's will ; be inverted in meaning... should have wired them directly from the P1 out ; bits to ground with 2.2K pullups to +5V, then a "0" would extinguish. ; ; To buffer the data coming from the HP a pair of 74'244 or 74'245 chips with ; the output enable(s) coming from an inverted P1.6 (d_data), the leftover ; inverter from the IDE interface will do. If using a '245 then the direction ; pin needs to be high or low to make it go in the proper direction (tbd by ; the layout, set so the input is coming from HP). Outputs of the '24x's ; are wired to the inputs of a pair of 74'374 chips for registers, and to the ; I/O data bits 0-15 with 470 ohm resistors in between to prevent contention ; damage if mis-programmed. The LED's on the Paul Rev5 board must be removed. ; Port bit P1.5 connects to the clock input of the '374's, the outputs of ; the '374's are always enabled. ; ; That pretty much describes the interface if connecting to a TTL interface ; such as a microcircuit board, the inputs (from the HP outs) should be ; pulled low with hi-val resistors (10K or so) esp if using CMOS parts. ; Some series resistance on every data, command and flag lines is probably ; a good idea. [perhaps 100 ohms, enough to limit current if miswired] ; ; A ground-true interface such as the "+16 BIT DUP REG" board I'm using for ; testing requires more circuitry. This board pulls inputs up to 8 volts with ; a ~800 ohm resistor for "0", grounded inputs read as "1" (~10ma required). ; Outputs are pulled up to 12 volts with a 10K resistor to output "0", and ; grounded by the interface to output a "1" with about 12ma sink capacity. ; The pinout of the interface looking at the back of the card inserted ; into the HP mini is... ; (cable side) ; ---- HP inputs, from IDE interface outputs ---- flag gnd ; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | .---- ; O O O O O O O O O O O O O O O O O O O O O O O O ; O O O O O O O O O O O O O O O O O O O O O O O O gnd ; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | `---- ; ---- HP outputs, to IDE interface inputs ------ command ; ; Each IDE interface input consists of a 220 ohm resistor from the HP out to ; the junction of a 8.2K resistor to ground, a switching diode to +5V, and the ; input of a 74'14 inverting trigger. This circuit is also used to buffer and ; invert the HP command output. For mine I used 2.2K and 10K. ; ; Each IDE interface output consists of a 1K resistor from the '374 output pin ; to the base of an NPN transistor, emitter grounded. Collector is connected ; to the HP input via a 10 ohm series resistor. This circuit is also used to ; buffer and invert the HP flag input. The flag out from the 8052 needs an ; emitter follower to ensure adequate current drive, with the 10K pin pullup ; I used 2.2K to the base of an NPN transistor, collector to +, emitter to ; a 1K to ground, 82 ohms from emitter to raw flag out to avoid damage if the ; line is shorted then onto the level shifter output circuit. [I don't know ; yet if this is TTL-board compatible, might need to lower the 1K emitter ; load to 470 ohms, or perhaps eliminate the follower and use a 3.3K pullup] ; ; For the "+16 BIT DUP REG" board the jumpers need to be set as follows... ; W4 to B, W5 to B (otherwise scrambled data! total mess if set to A...) ; W1 to +12, W2 to -12, W3 to -2, W6 to B, W7 to A ; W9-W12 out, W13 to A, W14 to A [I couldn't find these on the board I have ; resulting in a failure returning drive status, to work around the 25 us (reset pulse width) clr a movx @dptr, a ;no ide control lines asserted ret ;------------------------------------------------------------------ ; Some additional serial I/O routines not available in PAULMON ;print a 16 bit number, located at DPTR print_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 long print_name: movx a, @dptr jz pn_end lcall cout inc dptr djnz r0, print_name pn_end: ret ;get a 32 bit input, in hex ghex32_lba: mov r2, #8 mov r3, #0 mov r4, #0 mov r5, #0 mov r6, #0 gh32c: lcall cin_filter lcall upper cjne a, #27, gh32d setb c ret gh32d: cjne a, #8, gh32f sjmp gh32k gh32f: cjne a, #127, gh32g gh32k: cjne r2, #8, gh32e sjmp gh32c gh32e: lcall cout mov r0, #4 gh32yy: 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 gh32c gh32g: cjne a, #13, gh32i clr c ret gh32i: mov r7, a acall asc2hex jc gh32c xch a, r7 lcall cout mov a, r7 swap a mov r7, a mov r0, #4 gh32j: 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 input asc2hex: clr c add a, #208 jnc hex_not add a, #246 jc hex_maybe add a, #10 clr c ret hex_maybe: add a, #249 jnc hex_not add a, #250 jc hex_not add a, #16 clr c ret hex_not: setb c ret ;print a hexdump of the data in the 512 byte buffer hexdump: lcall newline mov r2, #32 ;print 32 lines mov dptr, #buffer hd1: 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 line hd2: 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 line hd3: movx a, @dptr inc dptr anl a, #01111111b ;only 7 bits for ascii cjne a, #127, hd3b clr a ;avoid 127/255 (delete/rubout) char hd3b: add a, #224 jc hd3c clr a ;avoid control characters hd3c: add a, #32 lcall cout djnz r3, hd3 lcall newline djnz r2, hd1 lcall newline ret space: mov a, #' ' ljmp cout ;------------------------------------------------------------------------ ;HP1000 disk controller statup code begins, initialize the interface regs dc_start: ;initialize the hp interface hardware clr a ;clear accumulator mov p1, a ;clear port1 signals mov p1_buf, a ;update iram port1 variable mov dptr, #hp_config ;get config address for hp interface mov a, #10000001b ;get hp 8255 write mode config byte (mod for SPI) movx @dptr, a ;write to hp interface 8255 ;clear the hp interface data registers mov dptr, #hp_porta ;get hp-8255 port a address clr a ;clear accumulator movx @dptr, a ;write 0's to port a mov dptr, #hp_portb ;get hp-8255 port b address movx @dptr, a ;write 0's to port b lcall ck_bus ;clock bus to hp data registers ;clear ide error code buffer clr a ;clear accumulator mov ide_error, a ;save in ide error code buffer ;reset disk controller mov blk, #0x00 ;zero blk+0 mov blk+1, #0x00 ;zero blk+1 mov blk+2, #0x00 ;zero blk+2 mov blk+3, #0x00 ;zero blk+3 lcall blk_2_lba ;update ATA address value ; clr acc ;clear accumulator clr a ;yes there's a difference! mov dc_len, a ;zero transfer lenght counter mov ide_error, a ;clear ide error code buffer ;check the front panel mode switch mov dptr, #hp_portc ;get hp-8255 port c address movx a, @dptr ;read port c into accumulator anl a, #00000001b ;mask out mode switch bit jnz hp_disk ;is switch up? ljmp debug ;no, start command line debugger ;------------------------------------------------------------------- ; HP1000 disk controller code begins here ;enter the disk controller command state, clear operate and set ;the command bits hp_disk: mov a, p1_buf ;get port1 state orl a, #00000001b ;set command led bit anl a, #11111101b ;clear operate led bit mov p1_buf, a ;save new buffer value mov p1, a ;send new value to port1 ;send a debugging message to the 8052 console ; mov dptr, #d_msg1 ;get command string address ; lcall pstr ;send it to the debug console ;**************************************************************** ; code added to send IDE controller status word to the HP ; interface while waiting to receive a new command. ;generate the disk controller status word mov a, p1_buf ;get port1 buffer value anl a, #0x10 ;mask out error bit only rl a rl a rl a ;move error bit to msb mov hp_msb, a ;save result to I/O buffer mov a, ide_error ;get ide error value mov hp_lsb, a ;save error value to I/O buffer ;clear d_data bit so 8255 can drive bus0-15 mov a, p1_buf ;get port1 state anl a, #10111111b ;clear d_data bit mov p1_buf, a ;save new port1 state mov p1, a ;send new value to port1 ;configure 8255 to drive bus0-15 mov dptr, #hp_config ;get hp 8255 config address mov a, #10000001b ;get output config byte (mod for SPI) movx @dptr, a ;write it to the config port ;send controller status to hp interface mov dptr, #hp_porta ;get hp 8255 porta address mov a, hp_lsb ;get ide status byte movx @dptr, a ;send it to bus0-7 mov dptr, #hp_portb ;get hp 8255 portb address mov a, hp_msb ;get disk controller status value movx @dptr, a ;send it to bus8-15 lcall ck_bus ;clock bus into hp data registers ;**************************************************************** ;get a command word from the hp interface lcall hp_2_ide ;get command word from hp ;---------------------------------------------------------------- ;enter the disk controller operate state, clear command and set ;the operate bits mov a, p1_buf ;get port1 value anl a, #11111110b ;clear command led bit orl a, #00000010b ;set operate led bit mov p1_buf, a ;save new port1 value mov p1, a ;send new value to port1 ;process disk command / parameter word mov a, hp_lsb ;get hp lsb, the parameter byte mov dc_para, a ;save hp parameter value mov a, hp_msb ;get hp msb, the command byte mov dc_cmd, a ;save hp command value ;------------------------------------------------------------------ ;Display HP disk controller debugging command and parameter bytes ; mov dptr, #d_msg2 ;get operate mode string address ; lcall pstr ;send it to the debug console ; mov a, dc_cmd ;get command byte ; lcall phex ;print it to the debug console ; mov dptr, #d_msg3 ;get parameter string address ; lcall pstr ;send it to the debug console ; mov a, dc_para ;get parameter byte ; lcall phex ;print it to the debug console ; lcall newline ;end of debug display line ;------------------------------------------------------------------ ;disk command execution begins dx0: mov a, dc_cmd ;get command value cjne a, #0x80, dx0b ;is it 0x80, Reset Disk Controller? ljmp rdc ;yes, jump to rdc code ;alternate status command 105xxx dx0b: cjne a,#0x8A, dx1 ;is it a status request? clr a mov hp_msb,a mov a, ide_error mov hp_lsb,a lcall ide_2_hp ;send to HP with handshaking ljmp hp_disk ;----------------------------------------------------------- ;its not reset, so check for any pre-existing error state dx1: mov a, p1_buf ;get port1 value anl a, #0x10 ;mask error bit only jnz dx_err ;got existing error state? ;no error set, proceed to process commands other than reset mov a, dc_cmd ;get command value cjne a, #0x82, dx2 ;is it 0x82, Set Block 1? ljmp sb1 ;yes, jump to sb1 code dx_err: ljmp dx_done ;error is set, accept only RDC! dx2: cjne a, #0x84, dx3 ;is it 0x84, Set Block 2? ljmp sb2 ;yes, jump to sb2 code dx3: cjne a, #0x86, dx4 ;is it 0x86, Set Block 3? ljmp sb3 ;yes, jump to sb3 code dx4: cjne a, #0x88, dx5 ;is it 0x88, Set Block 4? ljmp sb4 ;yes, jump to sb4 code dx5: cjne a, #0x90, dx6 ;is it 0x90, ReaD Blocks? ljmp rdb ;yes, jump to rdb code dx6: cjne a, #0x92, dx7 ;is it 0x92, WRite Blocks? ljmp wrb ;yes, jump to wrb code dx7: ;-------------------------------------------------------------- ; Add new disk controller command functions here ;access to SPI/USB interface... ;command 120xxx - read byte from SPI dx8: cjne a,#0xA0,dx9 ljmp dc_readSPIbyte ;command 121bbb - write byte bbb to SPI dx9: cjne a,#0xA2,dx10 ljmp dc_writeSPIbyte ;command 122xxx - sync with USB controller dx10: cjne a,#0xA4,dx11 ljmp dc_USBsync ;command 123xxx - clear remaining USB controller responses dx11: cjne a,#0xA6,dx12 ljmp dc_USBclear ;command 130xxx - open buffered read file ;filename+cr must be sent immediately afterwards dx12: cjne a,#0xB0,dx13 ljmp dc_openreadfile ;command 131xxx - read one byte from buffered file dx13: cjne a,#0xB2,dx14 ljmp dc_readfilebyte ;command 134xxx - open buffered write file ;filename+cr must be sent immediately afterwards dx14: cjne a,#0xB8,dx15 ljmp dc_openwritefile ;command 135bbb - write byte bbb to buffered file dx15: cjne a,#0xBA,dx16 ljmp dc_writefilebyte ;command 136xxx - close/flush buffered write file dx16: cjne a,#0xBC,dx17 ljmp dc_closewritefile ;command 137xxx - return usb_error value dx17: cjne a,#0xBE,dx18 ljmp dc_return_usb_error dx18: ;added 10/18/08... ;command not recognized, set ide_error variable to 255 ;otherwise bad command won't return an error with the new stat cmd mov a,#255 mov ide_error,a ;-------------------------------------------------------------- dx_error: ;unrecognised command byte detected! ;set disk controller error status bit ;send debugging status message ; mov dptr, #d_msg4 ;get error string address ; lcall pstr ;send it to the debug console ;set error state mov a, p1_buf ;get port1 buffer status orl a, #00010000b ;set error bit mov p1_buf, a ;save new buffer value mov p1, a ;send new value to port1 ;and complete HP command protocol dx_done: ;send controller status word to hp interface, an all zero ;word signals that the ide controller is ready and awaiting ;a valid command from the HP. lcall up_stat ;update controller status ljmp hp_disk ;and get another command ;------------------------------------------------------------------------ ;disk controller command execution functions begin... ;------------------------------------------------------------------------ ;RDC - Reset Disk Controller ;reset block, lba, lenght, and error values to zero, clear error state rdc: mov blk, #0x00 ;zero blk+0 mov blk+1, #0x00 ;zero blk+1 mov blk+2, #0x00 ;zero blk+2 mov blk+3, #0x00 ;zero blk+3 lcall blk_2_lba ;update ATA address value ; clr acc ;clear accumulator clr a mov dc_len, a ;zero transfer lenght counter mov ide_error, a ;clear ide error code buffer ;clear error state flag and led mov a, p1_buf ;get port1 state anl a, #11101111b ;clear error led mov p1_buf, a ;save new port1 value mov p1, a ;send new value to port1 ljmp dx_done ;end of RDC function ;----------------------------------------------------------------------- ;SB1 - Set Block 1 ;sets low 8 bits of 1K word block address sb1: mov a, dc_para ;get disk parameter byte mov blk, a ;save 8 bits of 1K word block address ; lcall p_blk ;display updated HP block address lcall blk_2_lba ;update the ata lba value ; lcall p_lba ;display updated lba address ljmp dx_done ;end of SB1 function ;----------------------------------------------------------------------- ;Set Block 2 ;sets next to least signifigent byte of 1K word block address sb2: mov a, dc_para ;get disk parameter byte mov blk+1, a ;save 8 bits of 1K word block address ; lcall p_blk ;display updated HP block address lcall blk_2_lba ;update the ata lba value ; lcall p_lba ;display updated lba address ljmp dx_done ;end of SB2 function ;----------------------------------------------------------------------- ;Set Block 3 ;sets next to most signifigent byte of 1K word block address sb3: mov a, dc_para ;get disk parameter byte mov blk+2, a ;save 8 bits of 1K word block address ; lcall p_blk ;display updated HP block address lcall blk_2_lba ;update the ata lba value ; lcall p_lba ;display updated lba address ljmp dx_done ;end of SB3 function ;----------------------------------------------------------------------- ;Set Block 4 ;sets top 2 bits of 1K word block address sb4: mov a, dc_para ;get disk parameter byte ;mask for valid 26-bit 1K word block address anl a, #00000011b ;mask off all invalid bits mov blk+3, a ;save 2 bits of 1K word block address ; lcall p_blk ;display updated HP block address lcall blk_2_lba ;update the ata lba value ; lcall p_lba ;display updated lba address ljmp dx_done ;end of SB4 function ;----------------------------------------------------------------------- ; RDB - ReaD Blocks. RDB with lenght = 0 will read one block ; from disk into buffer. RDB with lenght > 0 will read and ; transfer (lenght) 1K word blocks to the HP interface. rdb: ;echo command word back to host, d_data is still set! lcall ck_bus ;clock bus data into hp data registers ;complete the hp to ide command transfer protocol lcall ck_flg ;clock flag ;get transfer lenght value from parameter byte mov a, dc_para ;get transfer lenght ;mask to valid lenght and save transfer lenght value anl a, #00011111b ;mask off high 3 bits mov dc_len, a ;save new lenght value rb_loop: ;begin / continue read blocks operation ; lcall p_lba ;display the current lba address lcall rd_blk ;read 1 block from disk to buffer mov a, dc_len ;get current transfer lenght jz rb_done ;ready to transfer blocks? ;decrement transfer lenght mov a, dc_len ;get transfer lenght dec a ;decrement value mov dc_len, a ;save decremented value ;send 1K word of data to the HP lcall tx_blk ;send buffer to HP ;are we done with block transfers to HP? mov a, dc_len ;get transfer lenght jz rb_done ;done with block transfers ;no, more blocks remain to read and transfer ljmp rb_loop ;proceed with next block rb_done: ljmp hp_disk ;done with RDB function ;---------------------------------------------------------- ; WRB - Write Blocks. WRB with lenght = 0 will write one block ; from buffer onto disk. WRB with lenght > 0 will transfer ; and write (lenght) 1K word blocks from the HP interface to ; disk. wrb: ;echo command word back to host, d_data is still set! lcall ck_bus ;clock bus data into hp data registers ; complete the hp to ide command transfer protocol lcall ck_flg ;clock flag ;get transfer lenght value from parameter byte mov a, dc_para ;get transfer lenght ;mask for invalid lenght anl a, #00011111b ;mask off high 3 bits ;save new transfer lenght value mov dc_len, a ;save new lenght value ;is the transfer lenght equal to zero? mov a, dc_len ;get current transfer lenght jz xb_done ;any blocks to transfer? wb_loop: ;get 1K word of data from the host into the buffer lcall rx_blk ;get 1024 words from HP into buffer xb_done: ;write the current buffer contents to disk ; lcall p_lba ;display the current lba address lcall wr_blk ;write current buffer to disk ;is the transfer lenght now equal to zero? mov a, dc_len ;get current transfer lenght jz wb_done ;any more blocks to transfer? mov a, dc_len ;get current transfer lenght dec acc ;lenth = lenght - 1 mov dc_len, a ;save new transfer lenght jnz wb_loop ;more blocks to transfer? wb_done: ljmp hp_disk ;no, WRB function completed ;------------------------------------------------------------ up_stat: ;generate the disk controller status word mov a, p1_buf ;get port1 buffer value anl a, #0x10 ;mask out error bit only rl a rl a rl a ;move error bit to msb mov hp_msb, a ;save result to I/O buffer mov a, ide_error ;get ide error value mov hp_lsb, a ;save error value to I/O buffer ;clear d_data bit so 8255 can drive bus0-15 mov a, p1_buf ;get port1 state anl a, #10111111b ;clear d_data bit mov p1_buf, a ;save new port1 state mov p1, a ;send new value to port1 ;configure 8255 to drive bus0-15 mov dptr, #hp_config ;get hp 8255 config address mov a, #10000001b ;get output config byte (mod for SPI) movx @dptr, a ;write it to the config port ;send controller status to hp interface mov dptr, #hp_porta ;get hp 8255 porta address mov a, hp_lsb ;get ide status byte movx @dptr, a ;send it to bus0-7 mov dptr, #hp_portb ;get hp 8255 portb address mov a, hp_msb ;get disk controller status value movx @dptr, a ;send it to bus8-15 lcall ck_bus ;clock bus into hp data registers lcall ck_flg ;complete command transfer ret ;end of up_stat ;-------------------------------------------------------------------- blk_2_lba: mov a, blk ;get low byte of block address rl a ;rotate left one bit rl a ;rotate left another bit anl a, #11111100b ;mask off low 2 bits mov lba, a ;save in lba+0 ;ok so far, lba+0 is done, now claculate lba+1 mov a, blk ;get low byte again anl a, #11000000b ;keep high 2 bits rl a ;rotate left one bit rl a ;rotate left another bit mov tmp, a ;save in tmp+0 mov a, blk+1 ;get next higher byte rl a ;rotate left one bit rl a ;rotate left another bit anl a, #11111100b ;mask off low 2 bits orl a, tmp ;or in tmp+0 value mov lba+1, a ;save in lba+1 ;ok, lba+1 is now done, now calculate lba+2 mov a, blk+1 ;get next higher byte again anl a, #11000000b ;keep high 2 bits rl a ;rotate left one bit rl a ;roate left another bit mov tmp+1, a ;save in tmp+1 mov a, blk+2 ;get even higher byte rl a ;rotate left one bit rl a ;rotate left another bit anl a, #11111100b ;mask off low 2 bits orl a, tmp+1 ;or in tmp+1 value mov lba+2, a ;save in lba+2 ;then calculate the value for lba+3 mov a, blk+2 ;get even higher byte again anl a, #11000000b ;keep high 2 bits rl a ;rotate left one bit rl a ;rotate left another bit mov tmp+2, a ;save in tmp+2 mov a, blk+3 ;get top bits of 1K block address rl a ;rotate left one bit rl a ;rotate left another bit anl a, #11111100b ;mask off low 2 bits orl a, tmp+2 ;or-in tmp+2 value mov lba+3, a ;save in lba+3 ret ;end of blk_2_lba ;------------------------------------------------------------- ; 1K word logical block operations rd_blk: ;read 1024 16-bit words from drive into buffer lcall rd_sec_1 ;read first 512 byte lba lcall inc_lba ;increment lba pointer lcall rd_sec_2 ;read second 512 byte lba lcall inc_lba ;increment lba pointer lcall rd_sec_3 ;read third 512 byte lba lcall inc_lba ;increment lba pointer lcall rd_sec_4 ;read fourth 512 byte lba lcall inc_lba ;increment lba pointer ret ;end of read block wr_blk: ;write 1024 16-bit words from buffer to drive lcall wr_sec_1 ;write first 512 byte lba lcall inc_lba ;increment lba pointer lcall wr_sec_2 ;write second 512 byte lba lcall inc_lba ;increment lba pointer lcall wr_sec_3 ;write third 512 byte lba lcall inc_lba ;increment lba pointer lcall wr_sec_4 ;write fourth 512 byte lba lcall inc_lba ;increment lba pointer ret ;end of write block ;------------------------------------------------------------------- tx_blk: ;set the block output mode mov a, p1_buf ;get the port1 value orl a, #00001000b ;set block output bit mov p1_buf, a ;save the new port1 value mov p1, a ;send new value to port1 ;send 1024 16-bit words to HP with handshaking. mov dptr, #buff_1 ;point to start of disk buffer mov r5, #0 ;preset loop counter tx_c_loop: ;transfer word from the buffer to the host movx a, @dptr ;read msb from buffer mov hp_msb, a ;save to I/O buffer inc dptr ;point to lsb movx a, @dptr ;read lsb from buffer mov hp_lsb, a ;save to I/O buffer inc dptr ;point to msb of next word lcall ide_2_hp ;send the word to the HP ;transfer word from the buffer to the host movx a, @dptr ;read msb from buffer mov hp_msb, a ;save to I/O buffer inc dptr ;point to lsb movx a, @dptr ;read lsb from buffer mov hp_lsb, a ;save to I/O buffer inc dptr ;point to msb of next word lcall ide_2_hp ;send the word to the HP ;transfer word from the buffer to the host movx a, @dptr ;read msb from buffer mov hp_msb, a ;save to I/O buffer inc dptr ;point to lsb movx a, @dptr ;read lsb from buffer mov hp_lsb, a ;save to I/O buffer inc dptr ;point to msb of next word lcall ide_2_hp ;send the word to the HP ;transfer word from the buffer to the host movx a, @dptr ;read msb from buffer mov hp_msb, a ;save to I/O buffer inc dptr ;point to lsb movx a, @dptr ;read lsb from buffer mov hp_lsb, a ;save to I/O buffer inc dptr ;point to msb of next word lcall ide_2_hp ;send the word to the HP ;update and test 1K word block counter (R5) djnz r5, tx_c_loop ;repeat loop 256 times ;clear the block output mode and exit mov a, p1_buf ;get the port1 value anl a, #11110111b ;clear the block output bit mov p1_buf, a ;save the new value mov p1, a ;send it to port1 ret ;end of 1K word block transmit ;----------------------------------------------------------------- rx_blk: ;set the block input mode mov a, p1_buf ;get the port1 value orl a, #00000100b ;set the block input mode mov p1_buf, a ;save new port1 value mov p1, a ;send new value to port1 ;receive 1024 16-bit words from HP with handshaking. mov dptr, #buff_1 ;point to start of buffer mov r5, #0 ;preset loop counter rx_c_loop: ;transfer one word from the host into the disk data buffer lcall hp_2_ide ;get word from host lcall ck_flg ;acknowledge transfer mov a, hp_msb ;get msb from I/O buffer movx @dptr, a ;store into disk buffer inc dptr ;increment buffer pointer mov a, hp_lsb ;get lsb from I/O buffer movx @dptr, a ;store into buffer inc dptr ;increment buffer pointer ;transfer one word from the host into the buffer lcall hp_2_ide ;get word from host lcall ck_flg ;acknowledge transfer mov a, hp_msb ;get msb from I/O buffer movx @dptr, a ;store into disk buffer inc dptr ;increment buffer pointer mov a, hp_lsb ;get lsb from I/O buffer movx @dptr, a ;store into disk buffer inc dptr ;increment buffer pointer ;transfer one word from the host into the buffer lcall hp_2_ide ;get word from host lcall ck_flg ;acknowlege transfer mov a, hp_msb ;get msb from I/O buffer movx @dptr, a ;store into disk buffer inc dptr ;increment buffer pointer mov a, hp_lsb ;get lsb from I/O buffer movx @dptr, a ;store into disk buffer inc dptr ;increment buffer pointer ;transfer one word from the host into the buffer lcall hp_2_ide ;get word from host lcall ck_flg ;acknowlege transfer mov a, hp_msb ;get msb from I/O buffer movx @dptr, a ;store into disk buffer inc dptr ;increment buffer pointer mov a, hp_lsb ;get lsb from I/O buffer movx @dptr, a ;store into disk buffer inc dptr ;increment buffer pointer ;update and test 1K word block counter (R5) djnz r5, rx_c_loop ;repeat loop 256 times ;clear block input mode and exit mov a, p1_buf ;get port1 buffer value anl a, #11111011b ;clear block input bit mov p1_buf, a ;save new port1 value mov p1, a ;send new value to port1 ret ;end of 1K word block receive ;--------------------------------------------------------------- ; HP hardware interface functions ck_bus: ; clock bus0-15 into hp data registers mov a, p1_buf ;get port1 state orl a, #00100000b ;and latch data bit mov p1, a ;send to port1 mov a, p1_buf ;get port1 state mov p1, a ;restore original port1 state ret ;return ;---------------------------------------------------------------- ck_flg: ; clock the flag flip flop on hp interface board mov a, p1_buf ;get port1 state orl a, #10000000b ;or-in hp flag bit mov p1, a ;send to port1 mov a, p1_buf ;get port1 state mov p1, a ;restore original port1 state ret ;return ;----------------------------------------------------------------- w_cmd: ; wait for command signal from hp interface board mov dptr, #hp_portc ;get hp 8255 port c address w_cmd1: ;keep checking for hp_cmd movx a, @dptr ;read hp 8255 port c anl a, #00000010b ;mask hp-command signal jz w_cmd1 ;is hp_cmd asserted? ret ;yes, we are done ;----------------------------------------------------------------- hp_2_ide: ; get a word from the hp interface with handshaking ; the 16-bit word will be returned in hp_msb and ; hp_lsb buffers. does not set flag after the ; data is read, so the HP will keep waiting for ; the transfer to complete in skip-on-flag loop. ; d_data is left SET on exit, 8255 in input mode! ; save contents of dptr push dpl ;save low byte of dptr push dph ;save high byte of dptr ; configure 8255 to read bus0-15 mov dptr, #hp_config ;get hp 8255 config address mov a, #10010011b ;get input config byte (mod for SPI) movx @dptr, a ;write it to the config port ; set d_data bit so hp output data drives bus0-15 mov a, p1_buf ;get port1 state orl a, #01000000b ;or-in the d_data bit mov p1_buf, a ;save new port1 value mov p1, a ;send new value to port1 ; wait for device command signal from hp interface lcall w_cmd ;wait for hp device command signal ; read word from hp interface mov dptr, #hp_portb ;get hp 8255 port b address movx a, @dptr ;read bus8-15 into accumulator mov hp_msb, a ;save msb to buffer mov dptr, #hp_porta ;get hp 8255 port a address movx a, @dptr ;read bus0-7 into accumulator mov hp_lsb, a ;save lsb to buffer ; restore contents of dptr pop dph ;restore high byte of dptr pop dpl ;restore low byte of dptr ret ;end of hp_2_ide ide_2_hp: ; send a word to the hp interface with handshaking ; the 16-bit word to be sent must be in the hp_msb ; and hp_lsb data buffers. ; save contents of dptr push dpl ;save low byte of dptr push dph ;save high byte of dptr ; clear d_data bit so 8255 can drive bus0-15 mov a, p1_buf ;get port1 state anl a, #10111111b ;clear d_data bit mov p1_buf, a ;save new port1 state mov p1, a ;send new value to port1 ; configure 8255 to drive bus0-15 mov dptr, #hp_config ;get hp 8255 config address mov a, #10000001b ;get output config byte (mod for SPI) movx @dptr, a ;write it to the config port ; wait for device command signal from hp interface lcall w_cmd ;wait for cmd function ; send the word to bus0-15 mov dptr, #hp_porta ;get hp 8255 port a address mov a, hp_lsb ;get lsb of 16-bit word movx @dptr, a ;send it to hp 8255 port a mov dptr, #hp_portb ;get hp 8255 port b address mov a, hp_msb ;get msb of 16-bit word movx @dptr, a ;send it to hp 8255 port b ; clock the data into the hp data registers lcall ck_bus ;pulse the l_data signal ; complete the transfer by clocking flag lcall ck_flg ;clock flag to clear hp_cmd ; restore contents of dptr pop dph ;restore high byte of dptr pop dpl ;restore low byte of dptr ret ;end of ide_2_hp ;-------------------------------------------------------------- ; HP1000 interface hardware test functions for Debug Mode. ;-------------------------------------------------------------- int_tests: ;display the interface test menu and get user response mov dptr, #msg_2 ;get interface test menu string lcall pstr ;send it to the console lcall cin ;get users selection lcall upper ;convert to upper case ; test the users selection, was is (R)ead? cjne a, #'R', int2 ;was is (R)ead? ; yes, configure 8255 to read bus0-15 mov dptr, #hp_config ;get hp 8255 config address mov a, #10010011b ;get input config byte (mod for SPI) movx @dptr, a ;write it to the config port ; set d_data bit so hp output data drives bus0-15 mov a, p1_buf ;get port1 state orl a, #01000000b ;or-in the d_data bit mov p1_buf, a ;save new port1 value mov p1, a ;send new value to port1 ; read word from hp interface mov dptr, #hp_portb ;get hp 8255 port b address movx a, @dptr ;read bus8-15 into accumulator push acc ;push msb to stack mov dptr, #hp_porta ;get hp 8255 port a address movx a, @dptr ;read bus0-7 into accumulator push acc ;push lsb to stack ; clear d_data bit so 8255 data may drive bus0-15 mov a, p1_buf ;get port1 state anl a, #10111111b ;clear the d_data bit mov p1_buf, a ;save new port1 value mov p1, a ;send new value to port1 ; display hp data word read from hp mov dptr, #msg_rv ;get read value message address lcall pstr ;send it to the console pop dpl ;pop lsb from stack pop dph ;pop msb from stack lcall phex16 ;print 16 bit hex value lcall newline ;cr,lf sequence ljmp int_tests ;restart interface menu int2: cjne a, #'W', int3 ;no, was is (W)rite? ; yes, clear d_data bit so 8255 can drive bus0-15 mov a, p1_buf ;get port1 state anl a, #10111111b ;clear d_data bit mov p1_buf, a ;save new port1 state mov p1, a ;send new value to port1 ; configure 8255 to drive bus0-15 mov dptr, #hp_config ;get hp 8255 config address mov a, #10000001b ;get output config byte (mod for SPI) movx @dptr, a ;write it to the config port int2b: ; get 16-bit value to send mov dptr, #msg_wv ;get write prompt address lcall pstr ;send it to the console lcall ghex16 ;get data to send jc int2a ;got valid input? lcall newline ; write data to interface bus push dph ;save msb push dpl ;save lsb mov dptr, #hp_porta ;get hp 8255 porta address pop acc ;get lsb data movx @dptr, a ;send lsb to bus0-7 mov dptr, #hp_portb ;get hp 8255 portb address pop acc ;get msb data movx @dptr, a ;send msb to bus8-15 ; clock data into hp data registers lcall ck_bus ;clock data registers ljmp int_tests ;restart interface menu ; handle invalid hex input int2a: lcall newline ;get off bad input line ljmp int2b ;try again for valid input int3: cjne a, #'S', int4 lcall ck_flg ;clock the interface flag ljmp int_tests ;restart interface menu int4: cjne a, #'Q', int5 ljmp main_loop ;restart main menu int5: ljmp int_tests ;restart interface menu msg_2: .db "(R)ead (W)rite (S)et flag (Q)uit",13,10,0 msg_rv: .db "HP Data = ",0 msg_wv: .db "Disk Data = ",0 ;----------------------------------------------------------- ;increment the 4-byte logical block address value inc_lba: ; increment LBA pointer mov r3, lba+0 ; get low 8 bits of lba mov r4, lba+1 ; get next 8 bits mov r5, lba+2 ; get another 8 bits mov r6, lba+3 ; get top bits of lba mov a, r3 ; get byte inc a ; add one mov r3, a ; save low lba byte jz nxt_1 ; continue if result is zero sjmp nxt_lba_1 ; if not, we are done nxt_1: mov a, r4 ; get byte inc a ; add one mov r4, a ; save next lba byte jz nxt_2 ; continue if result is zero sjmp nxt_lba_1 ; if not, we are done nxt_2: mov a, r5 ; get byte inc a ; add one mov r5, a ; save next lba byte jz nxt_3 ; continue if result is zero sjmp nxt_lba_1 ; if not, we are done nxt_3: mov a, r6 ; get byte inc a ; add one mov r6, a ; save high lba byte nxt_lba_1: ; save and check for end of disk mov lba+0, r3 ; save low 8 bits of lba mov lba+1, r4 ; save next byte of lba mov lba+2, r5 mov lba+3, r6 ; save hi updated lba value ret ; return from inc_lba ;------------------------------------------------------------ ;break: ; ljmp break ;hang here for debugging! ;------------------------------------------------------------ d_msg1: .db "Command mode.",10,13,10,13,0 d_msg2: .db "Operate mode, command = ",0 d_msg3: .db " parameter = ",0 d_msg4: .db "Unrecognised command.",10,13,0 d_msg5: .db "ATA Logical Block Address = 0x",0 d_msg6: .db "HP Disk Block Address = 0x",0 ;-------------------------------------- ;Multi-sector (block) reads and writes! ;-------------------------------------- ;------------------------------------------------------------ ;read the first sector of a 1K word block, using the current ;lba address values. ;Return, acc is zero on success, non-zero for an error rd_sec_1: lcall ide_busy ;make sure drive is ready lcall wr_lba ;tell it which sector we want mov a, #ide_command mov r2, #ide_cmd_read lcall ide_wr ;ask the drive to read it lcall ide_drq ;wait until it's got the data jb acc.0, get_err_1 mov dptr, #buff_1 ;point to first 512 bytes of buffer lcall read_data ;grab the data clr a mov ide_error, a ;save success state ret get_err_1: mov a, #ide_err lcall ide_rd mov a, r2 jz gerr1 mov ide_error, a ;save ide error code lcall set_error ;set error bit ret gerr1: mov a, #255 mov ide_error, a ;save ide error code lcall set_error ;set error ret ;write the first sector of a 1K word block using the current ;lba address values. ;Return, acc is zero on success, non-zero for an error wr_sec_1: lcall ide_busy ;make sure drive is ready lcall wr_lba ;tell it which sector we want mov a, #ide_command mov r2, #ide_cmd_write lcall ide_wr ;tell drive to write a sector lcall ide_drq ;wait unit it wants the data jb acc.0, get_err_1 mov dptr, #buff_1 ;point to first 512 bytes of buffer lcall write_data ;give the data to the drive lcall ide_busy ;wait until the write is complete jb acc.0, get_err_1 clr a mov ide_error, a ;save success state ret ;-------------------------------------------------------------------- ;read the second sector of a 1K word block using the current lba ;address values. ;Return, acc is zero on success, non-zero for an error rd_sec_2: lcall ide_busy ;make sure drive is ready lcall wr_lba ;tell it which sector we want mov a, #ide_command mov r2, #ide_cmd_read lcall ide_wr ;ask the drive to read it lcall ide_drq ;wait until it's got the data jb acc.0, get_err_2 mov dptr, #buff_2 ;point to second 512 bytes of buffer lcall read_data ;grab the data clr a mov ide_error, a ;save success state ret get_err_2: mov a, #ide_err lcall ide_rd mov a, r2 jz gerr2 mov ide_error, a ;save ide error code lcall set_error ;set error bit ret gerr2: mov a, #255 mov ide_error, a ;save ide error code lcall set_error ;set error bit ret ;write the second sector of a 1K word block using the current ;lba address values. ;Return, acc is zero on success, non-zero for an error wr_sec_2: lcall ide_busy ;make sure drive is ready lcall wr_lba ;tell it which sector we want mov a, #ide_command mov r2, #ide_cmd_write lcall ide_wr ;tell drive to write a sector lcall ide_drq ;wait unit it wants the data jb acc.0, get_err_2 mov dptr, #buff_2 ;point to second 512 bytes of buffer lcall write_data ;give the data to the drive lcall ide_busy ;wait until the write is complete jb acc.0, get_err_2 clr a mov ide_error, a ;save success state ret ;-------------------------------------------------------------------- ;read the third sector of a 1K word block using the current lba ;address values. ;Return, acc is zero on success, non-zero for an error rd_sec_3: lcall ide_busy ;make sure drive is ready lcall wr_lba ;tell it which sector we want mov a, #ide_command mov r2, #ide_cmd_read lcall ide_wr ;ask the drive to read it lcall ide_drq ;wait until it's got the data jb acc.0, get_err_3 mov dptr, #buff_3 ;point to third 512 bytes of buffer lcall read_data ;grab the data clr a mov ide_error, a ;save success state ret get_err_3: mov a, #ide_err lcall ide_rd mov a, r2 jz gerr3 mov ide_error, a ;save ide error code lcall set_error ;set error bit ret gerr3: mov a, #255 mov ide_error, a ;save ide error code lcall set_error ;set error bit ret ;write the third sector of a 1K word block using the current lba ;address values. ;Return, acc is zero on success, non-zero for an error wr_sec_3: lcall ide_busy ;make sure drive is ready lcall wr_lba ;tell it which sector we want mov a, #ide_command mov r2, #ide_cmd_write lcall ide_wr ;tell drive to write a sector lcall ide_drq ;wait unit it wants the data jb acc.0, get_err_3 mov dptr, #buff_3 ;point to third 512 bytes of buffer lcall write_data ;give the data to the drive lcall ide_busy ;wait until the write is complete jb acc.0, get_err_4 clr a mov ide_error, a ;save success state ret ;-------------------------------------------------------------------- ;read the fourth sector of a 1K word block using the current lba ;address values. ;Return, acc is zero on success, non-zero for an error rd_sec_4: lcall ide_busy ;make sure drive is ready lcall wr_lba ;tell it which sector we want mov a, #ide_command mov r2, #ide_cmd_read lcall ide_wr ;ask the drive to read it lcall ide_drq ;wait until it's got the data jb acc.0, get_err_4 mov dptr, #buff_4 ;point to fourth 512 bytes of buffer lcall read_data ;grab the data clr a mov ide_error, a ;save success state ret get_err_4: mov a, #ide_err lcall ide_rd mov a, r2 jz gerr4 mov ide_error, a ;save ide error code lcall set_error ;set error bit ret gerr4: mov a, #255 mov ide_error, a ;save ide error code lcall set_error ;set error bit ret ;write the fourth sector of a 1K word block using the current lba ;address values. ;Return, acc is zero on success, non-zero for an error wr_sec_4: lcall ide_busy ;make sure drive is ready lcall wr_lba ;tell it which sector we want mov a, #ide_command mov r2, #ide_cmd_write lcall ide_wr ;tell drive to write a sector lcall ide_drq ;wait unit it wants the data jb acc.0, get_err_4 mov dptr, #buff_4 ;point to fourth 512 bytes of buffer lcall write_data ;give the data to the drive lcall ide_busy ;wait until the write is complete jb acc.0, get_err_4 clr a mov ide_error, a ;save success state ret ;------------------------------------------------------------------------ set_error: mov a, p1_buf ;get port1 state orl a, #00010000b ;set error bit mov p1_buf, a ;save new port1 value mov p1, a ;send new value to port1 ret ;end of set_error ;------------------------------------------------------------------------ p_lba: push dph ;save dph to stack push dpl ;save dpl to stack mov dptr, #d_msg5 ;point to lba address message lcall pstr ;send it to the console mov a, lba+3 lcall phex ;print lba msb mov a, lba+2 lcall phex ;print next lsb mov a, lba+1 lcall phex ;print next lsb mov a, lba+0 lcall phex ;print the lsb lcall newline ;send cr, lf to console pop dpl ;restore dpl pop dph ;restore dph ret ;end of p_lba ;-------------------------------------------------------------------------- p_blk: push dph ;save dph to stack push dpl ;save dpl to stack mov dptr, #d_msg6 ;point to block address message lcall pstr ;send it to the console mov a, blk+3 lcall phex ;print lba msb mov a, blk+2 lcall phex ;print next lsb mov a, blk+1 lcall phex ;print next lsb mov a, blk+0 lcall phex ;print the lsb lcall newline ;send cr, lf to console pop dpl ;restore dpl pop dph ;restore dph ret ;end of p_lba ; ============================================================================ ; USB hack to connect Vinculum VDRIVE2 module... ; PF.3 - input - read data ; PF.4 - output - write data ; PF.5 - output - SPI clock ; PF.6 - output - SPI chip-select ; ; Subroutines... ; initSPI - sets up SPI, leaves R7=0 and dptr=portF ; readSPIbyte - reads byte from SPI into R4, acc non-zero if invalid ; readSPIwait - calls readSPIbyte until valid ; writeSPIbyte - writes byte in R4 to SPI, acc non-zero if not accepted ; writeSPIwait - calls writeSPIbyte until accepted ; SPIsync - sends E and e commands until received in that order ; SPIclear - reads from SPI until no more immediate data ready ; SPIdelay - just a delay, uses R3 (presently 200 djnz cycles) ; These subs use acc,dptr and R3-R7. R7 and dptr must not ; be altered in between calls or else must be restored, dptr ; pointing to portF and R7 (normally) 0 (or recall initSPI). ; Acc,R3,R5,R6 are temps, can be altered but will be overwritten. ; ; Call USBprompt for a USB "dos" console (DIR MKD CD DLD DLF RD etc ; but *not* open/read/write commands), command must return output ; or it will hang (it's a feature:-). Prompt mode is kinda funny.. ; there's no easy way to determine the difference between end of output ; and pauses in output (both return status set), so when first starting ; or inserting a "disk" have to hit return for more output then extra ; "D:\>" prompts displayed. Helps to call SPIsync first but not done ; here to see startup output. ; ; Jump to SPImenu for debug menu (has its own Paulmon header) ; There's a lot of cruft in here including a prompt for running ; subroutines by (carefully!) entering hex addresses. .equ spicon, port2base+3 ;control register for SPI port .equ spiport, port2base+2 ;data register for SPI port .equ spiconv, 0x93 ;control value to access SPI port .equ spidi, 00001000b ;AND mask for read data bit .equ spido1, 00010000b ;OR mask to set write data bit .equ spido0, 11101111b ;AND mask to clear write data bit .equ spiclk1, 00100000b ;OR mask to set clock bit .equ spiclk0, 11011111b ;AND mask to clear clock bit .equ spisel1, 01010000b ;OR mask to select SPI device (sets data too) .equ spisel0, 10101111b ;AND mask to deselect SPI device (clears data too) .org usblocat .db 0xA5,0xE5,0xE0,0xA5 .db 35,255,0,0 .db 0,0,0,0 .db 0,0,0,0 .db 0,0,0,0 .db 0,0,0,0 .db 0,0,0,0 .db 255,255,255,255 .db "SPI/USB Functions",0 .org usblocat+64 .equ ghex, 0x003a mov sp, #stack ;in case not set up SPImenu: lcall initSPI ;drive SPI lines low lcall newline mov dptr, #SPIopts lcall pstr ;print SPI/USB debug menu SPImenu1: lcall cin lcall cout lcall newline cjne a,#'1',SPImenu2 mov dptr, #hexprompt lcall pstr lcall ghex SPImsend: mov r4, a lcall newline lcall initSPI lcall writeSPIbyte jz SPImenu mov dptr,#notacctext lcall pstr ljmp SPImenu SPImenu2: cjne a,#'2',SPImenu3 lcall initSPI lcall readSPIbyte push acc mov dptr, #hexrectext lcall pstr mov a, r4 lcall phex lcall newline pop acc jz SPImenu SPInotvalid: mov dptr, #notvalidtext lcall pstr ljmp SPImenu SPImenu3: cjne a,#'3',SPImenu4 mov dptr, #charprompt lcall pstr lcall cin lcall cout ljmp SPImsend SPImenu4: cjne a,#'4',SPImenu5 lcall initSPI lcall readSPIbyte push acc mov dptr, #charrectext lcall pstr mov a, r4 lcall cout lcall newline pop acc jnz SPInotvalid ljmp SPImenu SPImenu5: cjne a,#'5',SPImenu6 lcall USBprompt ljmp SPImenu SPImenu6: cjne a,#'6',SPImenu7 lcall USBsync ljmp SPImenu SPImenu7: cjne a,#'7',SPImenu8 ljmp RunSubroutine SPImenu8: cjne a,#'8',SPImenu99 ljmp 0 SPImenu99: ljmp SPImenu SPIopts: .db "1) Write hex byte to SPI 2) Receive hex byte from SPI",13,10 .db "3) Send character to SPI 4) Receive character from SPI",13,10 .db "5) Run USB DOS console 6) Run USB sync code",13,10 .db "7) Call Subroutine(s) 8) Exit (jump to 0)",13,10 .db "> ",0 hexprompt: .db "Enter hex value: ",0 charprompt: .db "Enter character: ",0 hexrectext: .db "Hex received = ",0 charrectext: .db "Char received = ",0 notacctext: .db "Not accepted",13,10,0 notvalidtext: .db "Not valid",13,10,0 initSPI: mov dptr, #spicon mov a, #spiconv movx @dptr, a mov dptr, #spiport mov r7, #0 clr a movx @dptr,a ljmp initSPI1 initSPI1: ljmp initSPI2 initSPI2: ljmp initSPI3 initSPI3: ljmp initSPI4 initSPI4: ret SPIdelay: mov r3, #200 SPIdelay1: djnz r3, SPIdelay1 ret ;the following commented bit set/reset subs and SPI read/write subs ;are easier to understand but are roughly twice as slow, optimized ;read/write code follows. ; ;setSPIclk: ; mov a, r7 ;get port buffer ; orl a, #spiclk1 ;set clock bit ; mov r7, a ;save it ; movx @dptr, a ;write to port ; ret ;clrSPIclk: ; mov a, r7 ; anl a, #spiclk0 ; mov r7, a ; movx @dptr, a ; ret ;selectSPI: ; mov a, r7 ; orl a, #spisel1 ; mov r7, a ; movx @dptr, a ; ret ;deselectSPI: ; mov a, r7 ; anl a, #spisel0 ; mov r7, a ; movx @dptr, a ; ret ;setSPIbit: ; mov a, r7 ; orl a, #spido1 ; mov r7, a ; movx @dptr, a ; ret ;clrSPIbit: ; mov a, r7 ; anl a, #spido0 ; mov r7, a ; movx @dptr, a ; ret ;readSPIbit: ; movx a, @dptr ; anl a, #spidi ; ret ; ;writeSPIbyte: ;; data to write in R4 ;; status returned in acc, 0=accepted ; mov a, r4 ; mov r6, a ;move data to send into R6 ; lcall deselectSPI ;make sure CS=0 ; lcall setSPIclk ; lcall clrSPIclk ; lcall setSPIclk ; lcall clrSPIclk ; lcall selectSPI ;set CS and data out ; lcall setSPIclk ; lcall clrSPIclk ; lcall clrSPIbit ;send 0 to write ; lcall setSPIclk ; lcall clrSPIclk ; lcall setSPIclk ;send another 0 for data reg ; lcall clrSPIclk ; mov r3, #8 ;do 8 bits ;SPIwloop: ; mov a, r6 ; rlc a ; mov r6, a ; jc SPIout1 ; lcall clrSPIbit ; ljmp SPIwclockit ;SPIout1: ; lcall setSPIbit ;SPIwclockit: ; lcall setSPIclk ; lcall clrSPIclk ; djnz r3, SPIwloop ; lcall readSPIbit ;get status bit ; mov r3, a ;save it ; lcall setSPIclk ; lcall clrSPIclk ; lcall deselectSPI ; lcall setSPIclk ; lcall clrSPIclk ; mov a, r3 ;get status ; ret ;readSPIbyte: ;; data returned in R4 ;; status returned in acc, 0=valid data ; lcall deselectSPI ;make sure CS=0 ; lcall setSPIclk ; lcall clrSPIclk ; lcall setSPIclk ; lcall clrSPIclk ; lcall selectSPI ;set CS and data out ; lcall setSPIclk ; lcall clrSPIclk ; lcall setSPIbit ;send 1 to read ; lcall setSPIclk ; lcall clrSPIclk ; lcall clrSPIbit ;send 0 for data reg ; lcall setSPIclk ; lcall clrSPIclk ; mov r4, #0 ; mov r3, #8 ;SPIrloop: ; mov a, r4 ; rl a ; mov r4, a ; lcall readSPIbit ; jz SPIrgoon ; mov a, r4 ; orl a, #1 ; mov r4, a ;SPIrgoon: ; lcall setSPIclk ; lcall clrSPIclk ; djnz r3, SPIrloop ; lcall readSPIbit ; mov r3, a ; lcall setSPIclk ; lcall clrSPIclk ; lcall deselectSPI ; lcall setSPIclk ; lcall clrSPIclk ; mov a, r3 ;return status in a ; ret ;0=read OK ; converted to inline subs and optimized... writeSPIbyte: ; data to write in R4 ; status returned in acc, 0=accepted mov a, r4 mov r6, a mov a, r7 anl a, #spisel0 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 movx @dptr, a orl a, #spisel1 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 movx @dptr, a anl a, #spido0 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 mov r7, a movx @dptr, a mov r3, #8 SPIwloop: mov a, r6 rlc a mov r6, a jc SPIout1 mov a, r7 anl a, #spido0 movx @dptr, a ljmp SPIwclockit SPIout1: mov a, r7 orl a, #spido1 movx @dptr, a SPIwclockit: orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 mov r7, a movx @dptr, a djnz r3, SPIwloop movx a, @dptr anl a, #spidi mov r3, a mov a, r7 orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 movx @dptr, a anl a, #spisel0 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 mov r7, a movx @dptr, a mov a, r3 ret writeSPIwait: ; ljmp wSPIw1 ;wSPIw1: ljmp wSPIw2 ;wSPIw2: lcall writeSPIbyte jnz writeSPIwait ret readSPIbyte: ; data returned in R4 ; status returned in acc, 0=valid data mov a, r7 anl a, #spisel0 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 movx @dptr, a orl a, #spisel1 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 movx @dptr, a orl a, #spido1 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 movx @dptr, a anl a, #spido0 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 mov r7, a movx @dptr, a mov r4, #0 mov r3, #8 SPIrloop: mov a, r4 rl a mov r4, a movx a, @dptr anl a, #spidi jz SPIrgoon mov a, r4 orl a, #1 mov r4, a SPIrgoon: mov a, r7 orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 mov r7, a movx @dptr, a djnz r3, SPIrloop movx a, @dptr anl a, #spidi mov r3, a mov a, r7 orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 movx @dptr, a anl a, #spisel0 movx @dptr, a orl a, #spiclk1 movx @dptr, a anl a, #spiclk0 mov r7, a movx @dptr, a mov a, r3 ret readSPIwait: ; ljmp rSPIw1 ;rSPIw1: ljmp rSPIw2 ;rSPIw2: lcall readSPIbyte jnz readSPIwait ret USBsync: ; Syncronize with VDRIVE2 device... lcall USBclear mov r4,#'E' lcall writeSPIwait mov r4,#13 lcall writeSPIwait lcall readSPIwait cjne r4,#'E',USBsync lcall readSPIwait cjne r4,#13,USBsync mov r4,#'e' lcall writeSPIwait mov r4,#13 lcall writeSPIwait lcall readSPIwait cjne r4,#'e',USBsync lcall readSPIwait cjne r4,#13,USBsync lcall USBclear ret ; empty buffer... ; note.. only clears remaining immediate response output ; does not clear if more output pending due to internal delay USBclear: lcall initSPI USBclear1: lcall readSPIbyte ljmp USBcw1 USBcw1: ljmp USBcw2 USBcw2: jz USBclear1 mov r6,#100 USBclear2: lcall SPIdelay lcall readSPIbyte jz USBclear1 djnz r6,USBclear2 ret USBptext: .db 13,10,"Raw USB console - do NOT use file write commands!!!" .db 13,10,"Press esc at prompt to exit",13,10,0 .equ vpromptchr, ':' USBprompt: mov dptr, #USBptext lcall pstr lcall initSPI mov r4, #'E' lcall writeSPIwait mov r4, #'C' lcall writeSPIwait mov r4, #'S' lcall writeSPIwait mov r4, #13 lcall writeSPIwait mov r4, #'I' lcall writeSPIwait mov r4, #'P' lcall writeSPIwait mov r4, #'A' lcall writeSPIwait mov r4, #13 lcall writeSPIwait mov r0, #0 ;reset LF-printed flag mov r1, #0 ;reset last char printed USBploop: lcall readSPIwait lcall chkCRaddLF ;add LF if missing from previous CR mov a, r4 lcall cout mov r1,a ;save last char printed USBp0: lcall initSPI lcall readSPIbyte jnz USBp1 USBpmore: lcall chkCRaddLF ;add LF if missing from previous CR mov a, r4 lcall cout mov r1,a ;save last char printed ljmp USBp0 USBp1: ;make sure it's really the end of output mov r6,#200 USBpwait: lcall SPIdelay lcall readSPIbyte jz USBpmore djnz r6, USBpwait lcall chkCRaddLF ;seems to be all it's going to send so get user input mov a, #vpromptchr lcall cout USBgetinput: lcall cin lcall cout cjne a, #27, USBp2 mov a,#32 ;space to cancel escape lcall cout lcall newline ret ;return to caller if esc pressed USBp2: mov r4, a lcall initSPI lcall writeSPIwait cjne r4, #13, USBgetinput ljmp USBploop chkCRaddLF: ;if last char (r1) was CR and current char (r4) is not LF ;then print the LF to be compatible with VT100 etc mov a,r1 cjne a,#13,chkCRdone ;prev char not a CR, exit mov a,r4 cjne a,#10,chkCRdoadd ;not a LF so add one chkCRdone: ret chkCRdoadd: mov a,#10 ;a linefeed lcall cout ;print it ret ;for debugging, jump to RunSubroutine to display a ;address prompt to call subroutines (initSPI, setSPIclk etc) ;use the assembly listing to determine addresses ;no provision (yet) for stuffing registers first ;be very careful when entering addresses! no way to back out ;of a mistake keypress. Enter 0000 to return to SPI debug menu. .equ myaddrl, 0x7A ;this is near top of stack .equ myaddrh, 0x7B .equ regsaveram, 0x2000 ;hopefully this doesn't clobber something RunSubWarningText: .db 13,10,"Warning!!! This function directly calls subroutines from" .db 13,10,"entered addresses. There is no editing, if a mistake is made" .db 13,10,"the only way out is reset. Incorrect usage can cause data loss!" .db 13,10,"Enter address 0000 to exit this wretched debugging prompt." .db 13,10,"Proceed with disaster? (Y/N) ",0 RunSubroutine: mov dptr,#RunSubWarningText lcall pstr RSyn1: lcall cin lcall upper cjne a,#'Y',RSyn2 lcall cout ljmp RSconfirmed RSyn2: cjne a,#'N',RSyn2 lcall cout lcall newline ljmp SPImenu RSconfirmed: lcall RunSub1 lcall newline ljmp SPImenu RunSubPrompt: .db "Enter sub address: ",0 RegDispDPTR: .db "DPTR = ",0 RegDispReg: .db "A/B/R0-R7 = ",0 SaveRegs: push b push acc mov a,dpl mov b,dph mov dptr,#regsaveram movx @dptr,a inc dptr mov a,b movx @dptr,a pop acc inc dptr movx @dptr,a pop b inc dptr mov a,b movx @dptr,a inc dptr mov a,R0 movx @dptr,a inc dptr mov a,R1 movx @dptr,a inc dptr mov a,R2 movx @dptr,a inc dptr mov a,R3 movx @dptr,a inc dptr mov a,R4 movx @dptr,a inc dptr mov a,R5 movx @dptr,a inc dptr mov a,R6 movx @dptr,a inc dptr mov a,R7 movx @dptr,a RestoreRegs: mov dptr,#regsaveram+4 movx a,@dptr mov R0,a inc dptr movx a,@dptr mov R1,a inc dptr movx a,@dptr mov R2,a inc dptr movx a,@dptr mov R3,a inc dptr movx a,@dptr mov R4,a inc dptr movx a,@dptr mov R5,a inc dptr movx a,@dptr mov R6,a inc dptr movx a,@dptr mov R7,a mov dptr,#regsaveram+2 movx a,@dptr push acc inc dptr movx a,@dptr push acc mov dptr,#regsaveram movx a,@dptr push acc inc dptr movx a,@dptr mov dph,a pop acc mov dpl,a pop b pop acc ret DisplayRegs: lcall SaveRegs lcall newline mov dptr, #RegDispDPTR lcall pstr lcall RestoreRegs lcall phex16 mov a,#32 lcall cout mov dptr, #RegDispReg lcall pstr mov dptr, #regsaveram+2 mov R7,#10 DispRegLoop: movx a,@dptr inc dptr lcall phex mov a,#32 lcall cout djnz R7,DispRegLoop lcall newline lcall RestoreRegs ret RunSub1: lcall DisplayRegs ;also saves mov dptr,#RunSubPrompt lcall pstr lcall ghex16 mov a,dpl jnz RunSub2 mov a,dph jnz RunSub2 lcall RestoreRegs ret RunSub2: lcall newline mov myaddrl,dpl mov myaddrh,dph lcall RestoreRegs lcall RunSub3 ljmp RunSub1 RunSub3: push myaddrl push myaddrh ret ;used this to make sure above mess worked TestSub: mov dptr,#testtext lcall pstr ret testtext: .db "It works!",13,10,0 ReadSpeedTestSub: ; this calls readSPIbyte 65536 times to estimate max speed mov dptr,#RSPStxt1 lcall pstr lcall initSPI mov R0,#255 SPTSloop1: mov R1,#255 SPTSloop2: lcall readSPIbyte djnz R1,SPTSloop2 djnz R0,SPTSloop1 mov dptr,#RSPStxt2 lcall pstr ret RSPStxt1: .db 13,10,"Calling readSPIbyte 65536 times",13,10,0 RSPStxt2: .db "Done.",13,10,0 ; ; USB applications follow.... ;---------------------------------------------------------------- ; ; Disk Image Save/Restore functions ; ; SaveDisk - saves IDE disk image to USB file DRIVE0.DSK ; RestoreDisk - restores IDE disk from USB file DRIVE0.DSK ; ; The SaveDisk code provides a choice of saving an entire 7906-sized platter ; (about 9.6 MB) or from 1 to 9 SFS volumes (64 files /~4.1 MB each). ; The RestoreDisk code restores however many complete 1KW blocks are in ; the image file, saving time when restoring from smaller disk images ; such as the IDE-compatible 7906 simulation disk (typ < 3MB). ; ; The bytes in each 16-bit word are swapped on save/restore for compatibility ; with the SimH HP2100 simulator's 7906 disk device, even if the image is ; pure IDE a 7906 build can be loaded to access the individual files. Due to ; the slowness of bit-banging SPI on a 8255 chip (takes about 25 minutes for ; a full 7906 platter at 22mhz) and to be SimH-compatible, save sizes greater ; than a 7906 platter are not implemented. For SFS this corresponds to 2 ; volumes of 64 files plus about 22 files from the 3rd volume, if using to ; back up a larger pure-IDE disk then make sure all important files are ; copied to the 1st 2 volumes. It wouldn't be hard to hack the code to ; save other amounts of data but if SimH compatibility isn't a factor it'd ; likely be faster to implement a USB device in the form of a PTR/PTP ; adapter using a microcontroller which can access the VDRIVE directly ; rather than through a peripheral interface, and stream in/out the data ; you want whether disk blocks or files. It's the complexity of the byte-swap ; code (pure IDE backups don't need to have the bytes swapped) and desire to ; be able to do it without the minicomputer running that makes it more ; desirable to put the image save/restore code into the disk controller ; itself, thus not burdoning HP-IPL/OS with extra code for this purpose. .equ buff_end,buff_1+2048 ;define end of 1KW buffer .equ blk7906,4932 ;number of 1KW blocks in a 7906-sized platter .equ blk1vol,2087 ;number of 1KW blocks in a single SFS volume (64 files) .equ blk2vol,4136 ;number of blocks in 2 volumes .equ blk3vol,6185 ;3 volumes .equ blk4vol,8234 ;etc .equ blk5vol,10283 .equ blk6vol,12332 .equ blk7vol,14381 .equ blk8vol,16430 .equ blk9vol,18479 .equ lbatermlow, 0x2E ;low ram variables for storing LBA terminate value .equ lbatermhigh, 0x2F SDsizeprompt: .db 13,10,"Save disk image to DRIVE0.DSK file on USB device. Select..." .db 13,10,"(P) Save 7906-sized platter (1-9) Save 1 to 9 SFS volume(s) ",0 SDconfirmtxt: .db 13,10,"This may take awhile and must not be interrupted. Continue? (Y/N) ",0 SDgotxt: .db 13,10,"Saving disk image...",13,10,0 IDEreaderrtxt: .db 13,10,"An IDE read error occurred",13,10,0 DIdonetxt: .db 13,10,"Operation complete",13,10,0 DIchktxt: .db 13,10,"Checking USB disk, please wait...",0 DIndtxt: .db 13,10,"USB disk not ready",13,10,0 DIerrtxt: .db 13,10,"An error occured",13,10,0 IFerrtxt: .db 13,10,"A file error occured, check USB disk",13,10,0 SaveDisk: clr a ;clear 4-byte block variable mov blk,a mov blk+1,a mov blk+2,a mov blk+3,a mov dptr,#SDsizeprompt lcall pstr lcall cin lcall upper cjne a,#'P',SDgsz1 lcall cout mov r0,#blk7906-((blk7906/256)*256) mov r1,#blk7906/256 ljmp SDconfirm SDgsz1: cjne a,#'1',SDgsz2 lcall cout mov r0,#blk1vol-((blk1vol/256)*256) mov r1,#blk1vol/256 ljmp SDconfirm SDgsz2: cjne a,#'2',SDgsz3 lcall cout mov r0,#blk2vol-((blk2vol/256)*256) mov r1,#blk2vol/256 ljmp SDconfirm SDgsz3: cjne a,#'3',SDgsz4 lcall cout mov r0,#blk3vol-((blk3vol/256)*256) mov r1,#blk3vol/256 ljmp SDconfirm SDgsz4: cjne a,#'4',SDgsz5 lcall cout mov r0,#blk4vol-((blk4vol/256)*256) mov r1,#blk4vol/256 ljmp SDconfirm SDgsz5: cjne a,#'5',SDgsz6 lcall cout mov r0,#blk5vol-((blk5vol/256)*256) mov r1,#blk5vol/256 ljmp SDconfirm SDgsz6: cjne a,#'6',SDgsz7 lcall cout mov r0,#blk6vol-((blk6vol/256)*256) mov r1,#blk6vol/256 ljmp SDconfirm SDgsz7: cjne a,#'7',SDgsz8 lcall cout mov r0,#blk7vol-((blk7vol/256)*256) mov r1,#blk7vol/256 ljmp SDconfirm SDgsz8: cjne a,#'8',SDgsz9 lcall cout mov r0,#blk8vol-((blk8vol/256)*256) mov r1,#blk8vol/256 ljmp SDconfirm SDgsz9: cjne a,#'9',SDgsznone lcall cout mov r0,#blk9vol-((blk9vol/256)*256) mov r1,#blk9vol/256 ljmp SDconfirm SDgsznone: ;no valid selection mov a,#32 lcall cout lcall newline lcall newline ret SDconfirm: mov a,r0 ;save selected save size mov blk,a mov a,r1 mov blk+1,a mov dptr,#SDconfirmtxt lcall pstr SDync1: lcall cin lcall upper cjne a,#'Y',SDync2 lcall cout ljmp SDstartsave SDync2: cjne a,#'N',SDync1 lcall cout lcall newline ret SDstartsave: lcall blk_2_lba ;convert blocks to LBA address (*4) mov a,lba mov lbatermlow,a ;set terminate variables mov a,lba+1 mov lbatermhigh,a ; do the requested disk image save... mov dptr,#DIchktxt lcall pstr lcall SelectSCSIPH ;init/sync and select short binary commands mov r4, #13 lcall writeSPIwait ;send a return to query disk lcall readSPIwait ;wait for response cjne r4,#'>',SDnotready lcall readSPIwait cjne r4,#13,SDnotready lcall SPIdelay ;buffer should now be empty lcall readSPIbyte ;but just in case make sure jz SDerror ;if not empty then something's wrong ljmp SDready SDnotready: lcall USBclear mov dptr,#DIndtxt lcall pstr ret SDerror: lcall USBclear mov dptr,#DIerrtxt lcall pstr ret SDready: mov dptr,#SDgotxt lcall pstr ;tell user the save is starting mov ide_error,#0 lcall ide_init ;initialize IDE drive clr a mov lba,a mov lba+1,a mov lba+2,a mov lba+3,a ;start at LBA 0 lcall initSPI ;initialize SPI interface ;if file exists delete it first (to avoid lost cluster) mov r4,#0x07 ;DLF command lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait lcall sendFNstring ;send filename string + CR lcall readSPIwait ;wait for file to be deleted (if it exists) lcall USBclear ;don't care about response ;open the output file on the USB disk for write mov r4,#0x09 ;OPW command lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait lcall sendFNstring lcall readSPIwait cjne r4,#'>',SDfileerror lcall readSPIwait mov r4,#0x28 ;seek to 0 (otherwise appends) lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait mov r4,#0 lcall writeSPIwait lcall writeSPIwait lcall writeSPIwait lcall writeSPIwait mov r4,#13 lcall writeSPIwait lcall readSPIwait cjne r4,#'>',SDfileerror lcall readSPIwait ljmp SDloop ;start save SDfileerror: ljmp IFerror ;close file and tell user about error SDloop: ;whenever lba (lsb) is 0 print a * for feedback ;entire drive platter will be 77 *'s ;(4932 1KW blocks, or 4D10 hex 512 byte sectors) mov a,lba jnz SDloop1 mov a,#'*' lcall cout SDloop1: ;check to see if done... mov a,lba cjne a,lbatermlow,SDloop2 mov a,lba+1 cjne a,lbatermhigh,SDloop2 ljmp SDdone ;save complete SDloop2: lcall rd_blk ;read 1KW block from disk into buff_1 to buff_4 mov a, ide_error ;get error byte jz SDloop3 ;if no error go on ljmp SDreaderror ;jump to error message if a read error occurs SDloop3: ;now have to byte-swap... lcall byteswap_1KW ;write 2048 byte byte-swapped buffer to file... lcall initSPI ;point dptr etc to USB mov r4,#0x08 ;WRF command lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait mov r4,#0 lcall writeSPIwait lcall writeSPIwait mov r4,#8 lcall writeSPIwait mov r4,#0 lcall writeSPIwait ;2048 bytes mov r4,#13 lcall writeSPIwait mov dptr, #buff_1 ;point dptr to buffer SDfwloop: mov a,dpl cjne a,#buff_end-((buff_end/256)*256),SDfw1 mov a,dph cjne a,#buff_end/256,SDfw1 ljmp SDfwdone ;done writing 2048 bytes SDfw1: movx a,@dptr ;get byte mov r4,a ;put in spi data reg push dpl push dph ;save buffer ptr lcall initSPI ;set dptr to USB port lcall writeSPIwait ;write to USB pop dph pop dpl ;restore buffer ptr inc dptr ;point to next buffer byte ljmp SDfwloop ;loop until all bytes written SDfwdone: lcall initSPI lcall readSPIwait ;get response, should be > cjne r4,#'>',IFerrjmp ;if not branch to error lcall readSPIwait ;get and discard cr ljmp SDloop ;keep going to read/swap/write more SDdone: lcall IFclose ;close the image file cjne r1,#'>',IFerrjmp ;if not > then a close error occurred mov dptr,#DIdonetxt lcall pstr ;success ret IFerrjmp: ljmp IFerror SDreaderror: mov dptr,#IDEreaderrtxt ;IDE read error message lcall pstr lcall IFclose ret sendFNstring: ;sends the name of the image file + cr mov r4,#'D' lcall writeSPIwait mov r4,#'R' lcall writeSPIwait mov r4,#'I' lcall writeSPIwait mov r4,#'V' lcall writeSPIwait mov r4,#'E' lcall writeSPIwait mov r4,#'0' lcall writeSPIwait mov r4,#'.' lcall writeSPIwait mov r4,#'D' lcall writeSPIwait mov r4,#'S' lcall writeSPIwait mov r4,#'K' lcall writeSPIwait mov r4,#13 lcall writeSPIwait ret IFclose: ;close disk image file lcall initSPI ;point dptr to USB lcall USBclear ;make sure USB read buffer is clear mov r4,#0x0A ;CLF command lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait lcall sendFNstring lcall readSPIwait ;wait for file to close mov a,r4 mov r1,a ;return > or something else in R1 lcall USBclear ;clear rest of prompt ret IFerror: lcall USBclear mov dptr,#IFerrtxt ;file error message lcall pstr lcall IFclose ret ;exit subroutine byteswap_1KW: mov dptr,#buff_1 ;start at beginning of buffer bs1kwloop: mov a,dpl cjne a,#buff_end-((buff_end/256)*256),bs1kw1 mov a,dph cjne a,#buff_end/256,bs1kw1 ret ;done byte-swapping bs1kw1: movx a,@dptr mov r0,a push dpl push dph inc dptr movx a,@dptr pop dph pop dpl movx @dptr,a inc dptr mov a,r0 movx @dptr,a inc dptr ljmp bs1kwloop SelectSCSIPH: ;select short command set, binary input/output lcall USBsync ;initSPI and get USB device ready for access mov r4, #'S' lcall writeSPIwait mov r4, #'C' lcall writeSPIwait mov r4, #'S' lcall writeSPIwait mov r4, #13 lcall writeSPIwait lcall USBclear mov r4, #0x91 lcall writeSPIwait mov r4, #0x0D lcall writeSPIwait lcall USBclear ret ; Restore disk image... ; This subroutine copies the DRIVE0.DSK file on the USB device ; to the IDE disk. Only complete 1KW blocks that exist in the ; file are transferred, saving time when transferring images ; smaller than a complete 7906-sized disk platter. File is read ; using the RD command, end-of-transfer is detected by (in SCS mode) ; [cr] followed by invalid bytes. Bytes are swapped. ; RDprompt: .db 13,10,"Restore disk image from DRIVE0.DSK file on USB device." .db " Continue? (Y/N) ",0 RDgotxt: .db 13,10,"Restoring disk image... Do not interrupt...",13,10,0 RDnottxt: .db "Valid image file not found",13,10,0 IDEwrerrtxt: .db 13,10,"IDE disk write error, clearing USB buffer...",13,10,0 RestoreDisk: mov dptr,#RDprompt lcall pstr RDync1: lcall cin lcall upper cjne a,#'Y',RDync2 lcall cout ljmp RDdoit RDync2: cjne a,#'N',RDync1 lcall cout lcall newline ret RDdoit: mov dptr,#DIchktxt lcall pstr lcall SelectSCSIPH ;init/sync and select short binary commands mov r4, #13 lcall writeSPIwait ;send a return to query disk lcall readSPIwait ;wait for response cjne r4,#'>',RDnotready lcall readSPIwait cjne r4,#13,RDnotready lcall SPIdelay ;buffer should now be empty lcall readSPIbyte ;but just in case make sure jz RDerror ;if not empty then something's wrong ljmp RDready RDnotready: mov dptr,#DIndtxt lcall pstr ret RDerror: mov dptr,#DIerrtxt lcall pstr ret RDready: mov dptr,#RDgotxt lcall pstr ;tell user the restore is starting mov ide_error,#0 lcall ide_init ;initialize IDE drive clr a mov lba,a mov lba+1,a mov lba+2,a mov lba+3,a ;start at LBA 0 lcall initSPI mov r4,#0x04 ;RD command lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait lcall sendFNstring ;send filename string + cr ;read command sent, buffer bytes returned until CR followed by many ;invalid bytes (with time delay inbetween reads). When 1KW buffer fills, ;swap the bytes and write to disk. RDblockloop: mov dptr,#buff_1 ;point to start of 1KW buffer RDgetbyte: push dpl push dph lcall initSPI lcall readSPIbyte pop dph pop dpl jz RDbytevalid ;if valid read store in buffer cjne r4,#13,RDgetbyte ;if not a CR try again mov r1,#200 ;200 tries to become valid RDcheckforeof: mov r0,#50 ;call SPIdelay 50 times RDcheckeofwait: lcall SPIdelay djnz r0,RDcheckeofwait push dpl push dph lcall initSPI lcall readSPIbyte ;try again pop dph pop dpl jz RDbytevalid ;if valid keep going djnz r1,RDcheckforeof ;wait some more until timeout ljmp RDdone ;no more data assumed to be coming, done RDbytevalid: mov a,r4 movx @dptr,a inc dptr ;next buffer location mov a,dpl ;if not 1KW get more bytes... cjne a,#buff_end-((buff_end/256)*256),RDgetbyte mov a,dph cjne a,#buff_end/256,RDgetbyte ;buffer full, indicate, swap and write to disk mov a,lba jnz RDswapandwrite mov a,#'*' lcall cout RDswapandwrite: lcall byteswap_1KW lcall wr_blk ;write 1KW block to IDE disk mov a,ide_error ;get status jz RDblockloop ;if no error keep going mov dptr,#IDEwrerrtxt lcall pstr ;display error message lcall initSPI ;back to USB port and lcall USBclear ;absorb rest of file ret RDdone: ;check to see if anything was saved... mov a,lba cjne a,#0,RDdonesuccess mov a,lba+1 cjne a,#0,RDdonesuccess mov a,lba+2 cjne a,#0,RDdonesuccess mov a,lba+3 cjne a,#0,RDdonesuccess mov dptr,#RDnottxt ;file not valid message lcall pstr ret RDdonesuccess: mov dptr,#DIdonetxt ;success message lcall pstr ret ;---------------------------------------------------------------- ;code for USB-related IDE disk commands... ;reads byte from the SPI interface and sends it to the HP ;returns 100000 if data is not valid ;this tolerates minor pauses in output, usually >256 means the end ;of output unless busy processing a command that takes awhile dc_readSPIbyte: lcall initSPI ;initialize SPI interface dx8a: lcall readSPIbyte ;read byte from SPI jz dx8c ;if valid send to HP mov r6,#200 ;timeout value dx8b: lcall SPIdelay ;delay a bit lcall readSPIbyte ;try again jz dx8c ;if valid send to HP djnz r6,dx8b ;keep trying until timeout mov a,#0x80 mov hp_msb,a ;set b15 clr a mov hp_lsb,a ;clear lsb ljmp dx8d ;send invalid to HP dx8c: clr a mov hp_msb,a ;clear msb to indicate valid mov a,r4 ;get byte returned by SPI mov hp_lsb,a ;into lsb dx8d: lcall ide_2_hp ;send to hp with handshaking ljmp hp_disk ;writes byte from the HP (in the low 8 bits of command) ;to the SPI interface, waits for the byte to be accepted ;before further IDE commands can be processed dc_writeSPIbyte: lcall initSPI mov a,dc_para ;get lower 8 bits mov r4,a ;put in SPI send reg lcall writeSPIwait ;send byte, wait until accepted ljmp dx_done ;run this to ensure all startup output is cleared and ;received responses correspond with the command just given ;IDE commands not processed until the process completes dc_USBsync: lcall USBsync ljmp dx_done ;this clears any remaining immediate response output ;it does not clear delayed responses so don't use after ;commands which might cause a processing delay dc_USBclear: lcall USBclear ljmp dx_done ;---------------------------------------- ;buffered file access... the stream system is implemented ;using separate read and write buffers, the files do not ;remain open, rather they are opened and closed as needed ;to fill or empty the buffer. This results in delays but ;provides the essential illusion of two files open at once. ;It also helps prevent file system damage if the thumbdrive ;is removed or the close-file command not given. USB commands ;can be sent while buffered files are "open" but make sure ;no files are open when reading or writing buffered bytes ;in case a buffer fill or flush is triggered. ;usbbufbase,readbufsize,writebufsize,usbvarbase defined at top of source .equ readbuffer, usbbufbase ;read buffer .equ readbufterm, readbuffer+readbufsize ;end of read buffer plus 1 .equ writebuffer, readbufterm ;write buffer right after read buffer .equ writebufterm, writebuffer+writebufsize ;end of write buffer plus 1 .equ readfilename, usbvarbase ;read filename followed by cr (0 if none) .equ readfilechk, usbvarbase+0x0D ;3 bytes to detect uninitialized read filename .equ writefilename, usbvarbase+0x10 ;write filename followed by cr (0 if none) .equ writefilechk, usbvarbase+0x1D ;3 bytes to detect uninitialized write filename .equ readfilesize, usbvarbase+0x20 ;4 bytes with size of read file lsb to msb .equ readfileptr, usbvarbase+0x24 ;4 bytes with current read pointer lsb to msb .equ readbufptr, usbvarbase+0x28 ;2 bytes with buffer address of next read lsb,msb .equ writebufptr, usbvarbase+0x2A ;2 bytes with buffer address of next write lsb,msb .equ writebufcnt, usbvarbase+0x2C ;2 bytes with #bytes in write buffer (to avoid math) .equ usb_error, usbvarbase+0x30 ;error code for USB operations .equ usb_debug, usbvarbase+0x40 ;for debugging use ;usb_error values... .equ usb_nderror, 1 ;no disk .equ usb_cferror, 2 ;command failed (not found or a dir) .equ usb_fnerror, 3 ;invalid filename .equ usb_foerror, 4 ;file already open .equ usb_dferror, 5 ;disk full .equ usb_stierror, 6 ;streaming input buffer not open .equ usb_stoerror, 7 ;streaming output buffer not open .equ usb_eoferror, 8 ;past end of input file .equ usb_unkerror, 255 ;some other error occured .equ dcvar1, 0x2C ;temp variables .equ dcvar2, 0x2D .equ dcvar3, 0x2E .equ dcvar4, 0x2F ; uncomment if debugging sets needed... ;SetUSBdebug: ;;put a in usb_debug ;;insert mov a,#number/lcall SetUSBdebug to set a debug code ; push dpl ; push dph ; mov dptr,#usb_debug ; movx @dptr,a ; pop dph ; pop dpl ; ret ;;some pre-canned sets... ;SetDebug1: ; push acc ; mov a,#1 ; lcall SetUSBdebug ; pop acc ; ret ;etc SendSPIstring: ;sends string pointed to by dptr to SPI up to and including CR movx a,@dptr inc dptr mov r4,a push dpl push dph lcall initSPI lcall writeSPIwait pop dph pop dpl mov a,r4 cjne a,#13,SendSPIstring ret ;-------- subroutines for streaming IDE commands ---------- FillReadBuffer: ;open readfilename at readfileptr (assumed valid), ;read in readbufsize bytes to readbuffer, close the file, ;set readbufptr to address of readbuffer. If error occurs then ;mark readfilename invalid by clearing readfilechk byte ;and set usb_error appropriately (or just to unknown, obvious ;errors should already have been triggered by this time) lcall SelectSCSIPH ;init/sync/select short commands mov r4,#0x0E ;OPR - open for read lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait mov dptr,#readfilename lcall SendSPIstring ;send read filename + cr lcall initSPI lcall readSPIwait mov a,r4 cjne a,#'>',FRB_error1 lcall readSPIwait mov a,r4 cjne a,#13,FRB_error1 ljmp FRB_seek FRB_error1: ljmp FRB_error_noclose FRB_seek: ;open for read succeeded, seek to current file position... mov r4,#0x28 ;SEK - seek to position lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait ;send read file pointer, msb to lsb (in mem lsb to msb) mov dptr,#readfileptr movx a,@dptr push acc inc dptr movx a,@dptr push acc inc dptr movx a,@dptr push acc inc dptr movx a,@dptr mov r4,a lcall initSPI lcall writeSPIwait pop acc mov r4,a lcall writeSPIwait pop acc mov r4,a lcall writeSPIwait pop acc mov r4,a lcall writeSPIwait mov r4,#13 lcall writeSPIwait lcall readSPIwait ;check seek response mov a,r4 cjne a,#'>',FRB_error lcall readSPIwait mov a,r4 cjne a,#13,FRB_error ;fill buffer... mov r4,#0x0B ;RDF - read bytes from file lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait mov r4,#0 lcall writeSPIwait lcall writeSPIwait mov r4,#readbufsize/256 lcall writeSPIwait mov r4,#readbufsize-((readbufsize/256)*256) lcall writeSPIwait mov r4,#13 lcall writeSPIwait ;request number of bytes to fill buffer ljmp FRB_fillbuffer FRB_error: lcall FRB_closefile FRB_error_noclose: mov a,#usb_unkerror mov dptr,#usb_error movx @dptr,a ;set error value clr a mov dptr,#readfilechk movx @dptr,a ;invalidate read filename lcall USBclear ;clear response buffer ret ;exit sub FRB_fillbuffer: mov dptr,#readbuffer FRB_loop: push dpl push dph lcall initSPI lcall readSPIwait pop dph pop dpl mov a,r4 movx @dptr,a inc dptr mov a,dpl cjne a,#readbufterm-((readbufterm/256)*256),FRB_loop mov a,dph cjne a,#readbufterm/256,FRB_loop ;buffer loaded, should check for proper response - only that's a bit ;tough since if filling the buffer from a short file or at the end ;of the file produces "command failed" error - so that's normal lcall initSPI lcall readSPIwait mov a,r4 cjne a,#'>',FRB_cr1 ljmp FRB_cr2 ;if a > then not at EOF, soak up one more char FRB_cr1: cjne a,#'C',FRB_error ;if not a C then error lcall readSPIwait ;discard F FRB_cr2: lcall readSPIwait ;discard CR ;close file, set buffer pointer and return lcall FRB_closefile mov dptr,#readbufptr mov a,#readbuffer-((readbuffer/256)*256) movx @dptr,a inc dptr mov a,#readbuffer/256 movx @dptr,a ret FRB_closefile: lcall initSPI mov r4,#0x0A ;CLF - close file lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait mov dptr,#readfilename lcall SendSPIstring lcall initSPI lcall readSPIwait mov a,r4 cjne a,#'>',FRB_error2 lcall readSPIwait mov a,r4 cjne a,#13,FRB_error2 ret FRB_error2: ljmp FRB_error_noclose ;will return from this GetFilenameFromHP: ;call with dptr set to filename memory lcall ck_flg ;tell HP last command was accepted mov r1,#12 ;max 12 chars in filename dc_getfnloop: mov a,r1 push acc ;hp_2_ide preserves regs but a but just in case push dpl push dph lcall hp_2_ide ;get word from HP into hp_msb and hp_lsb lcall ck_flg ;tell the HP we got it pop dph pop dpl pop acc mov r1,a mov a,hp_lsb ;get fn char movx @dptr,a ;store in read filename mov a,r1 ;get char countdown jz dc_getfn1 ;if too many chars don't increment pointer inc dptr ;point to next position dec r1 ;dec char count dc_getfn1: mov a,hp_lsb ;get last fn char cjne a,#13,dc_getfnloop ;if not cr loop until one received ;filenames longer than 12 characters are truncated ret FlushWriteBuffer: ;if writebufcnt = 0 then do nothing, otherwise append writebufcnt bytes ;from writebuffer to file specified by writefilename, then set writebufptr ;to beginning of writebuffer and clear writebufcnt. If an error occurs mark ;write file invalid to prevent further writes and set usb_error. mov dptr, #writebufcnt movx a,@dptr jnz FWB_gotbytes inc dptr movx a,@dptr jnz FWB_gotbytes ret ;nothing to do FWB_gotbytes: lcall SelectSCSIPH ;init/sync/select short commands mov r4,#0x09 ;OPW - open file for write lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait mov dptr,#writefilename lcall SendSPIstring ;send write filename + cr lcall initSPI lcall readSPIwait mov a,r4 cjne a,#'>',FWB_parse_error lcall readSPIwait ;read and discard cr ljmp FWB_writebuffer FWB_parse_error: mov a,r4 ;get return cjne a,#'D',FWB_unknown_error mov a,#usb_dferror ;disk full ljmp FWB_seterror FWB_unknown_error: mov a,#usb_unkerror FWB_seterror: mov dptr,#usb_error movx @dptr,a clr a mov dptr,#writefilechk movx @dptr,a ;invalidate write file lcall USBclear ;clear response buffer ret ;exit sub FWB_writebuffer: ;open succeeded, send write to file command mov dptr,#writebufcnt movx a,@dptr mov r0,a ;lsb of count in r0 inc dptr movx a,@dptr mov r1,a ;msb of count in r1 lcall initSPI ;dptr back to SPI port mov r4,#0x08 ;WRF - write to file lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait mov r4,#0 lcall writeSPIwait lcall writeSPIwait mov a,r1 mov r4,a lcall writeSPIwait ;send msb of count mov a,r0 mov r4,a lcall writeSPIwait ;send lsb of count mov r4,#13 lcall writeSPIwait ;now send that many bytes, count still in r0 (lsb) r1 (msb) mov dptr,#writebuffer FWB_loop: movx a,@dptr mov r4,a push dpl push dph lcall initSPI lcall writeSPIwait pop dph pop dpl inc dptr dec r0 ;decrement lsb of count mov a,r0 ;get it cjne a,#255,FWB_loop1 ;if it rolls over dec r1 ;decrement msb of count FWB_loop1: mov a,r1 jnz FWB_loop mov a,r0 jnz FWB_loop ;loop until countdown = 0 ;all bytes in buffer sent, response should be > ;only error detected is disk full, all others unknown lcall initSPI lcall readSPIwait mov a,r4 cjne a,#'>',FWB_write_error ;jump if error lcall readSPIwait ;absorb cr ;reset write byte count clr a mov dptr,#writebufcnt movx @dptr,a inc dptr movx @dptr,a ;reset buffer pointer mov dptr,#writebufptr mov a,#writebuffer-((writebuffer/256)*256) movx @dptr,a inc dptr mov a,#writebuffer/256 movx @dptr,a ;close file and return ljmp FWB_closefile ;will return from sub there FWB_write_error: lcall FWB_parse_error ;set usb_error and mark file invalid FWB_closefile: lcall initSPI mov r4,#0x0A ;CLF - close file lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait mov dptr,#writefilename lcall SendSPIstring ;send write filename + cr lcall initSPI lcall readSPIwait mov a,r4 cjne a,#'>',FWB_close_error ;jump if error lcall readSPIwait ;absorb cr ret ;done FWB_close_error: ljmp FWB_parse_error ;will return from there ;-------- code for streaming IDE commands ------------ dc_openreadfile: ;open buffered file for reading, filename[cr] must be ;sent immediately after sending command. Sending an empty filename ;(just cr) abandons the output buffer and clears the filename. clr a mov dptr,#usb_error movx @dptr,a ;clear usb_error value mov dptr,#readfilename lcall GetFilenameFromHP mov dptr,#readfilename movx a,@dptr cjne a,#13,dc_orf_tryopen ;go on if non-empty string dc_orf_invalidexit: clr a mov dptr,#readfilechk movx @dptr,a ;invalidate read filename lcall USBclear ;empty response buffer ljmp hp_disk dc_orf_tryopen: lcall SelectSCSIPH ;init/sync and select short binary commands mov r4,#0x01 ;DIR command lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait mov dptr,#readfilename ;point to read filename just saved lcall SendSPIstring ;send filename + cr lcall initSPI ;back to SPI port lcall readSPIwait ;wait for response ;if file exists output will be [cr][filename] abcd[cr] where abcd is ;file size lsb to msb. If a directory then abcd = all zero. ;possible VDRIVE2 error returns are [cr]CF[cr] if file doesn't exist, ;[cr]FO[cr] if file open, [cr]ND[cr] if no disk, [cr]FN[cr] if ;filename invalid. But they all start with CR, complicating error ;detection. Instead.. save 2 chars after 1st CR, parse to either CR ;or space - if CR detected before space then it's an error otherwise ;next 4 bytes after space is filesize, lsb to msb. mov a,r4 cjne a,#13,dc_orf_unkerror ljmp dc_orf_parsedirresponse dc_orf_unkerror: ;cr not returned by dir... don't know why mov a,#usb_unkerror dc_orf_seterror: mov dptr,#usb_error movx @dptr,a ;set error value ljmp dc_orf_invalidexit ;invalidate read filename and exit dc_orf_pdrseterror: ;set error response... mov a,r0 ;get 1st saved return char cjne a,#'C',dc_orf_pdrse1 mov a,#usb_cferror ljmp dc_orf_seterror dc_orf_pdrse1: cjne a,#'F',dc_orf_pdrse3 mov a,r1 ;get 2nd saved return char cjne a,#'N',dc_orf_pdrse2 mov a,#usb_fnerror ljmp dc_orf_seterror dc_orf_pdrse2: cjne a,#'O',dc_orf_unkerror mov a,#usb_foerror ljmp dc_orf_seterror dc_orf_pdrse3: cjne a,#'N',dc_orf_unkerror mov a,#usb_nderror ljmp dc_orf_seterror dc_orf_parsedirresponse: lcall readSPIwait ;get next char mov a,r4 mov r0,a ;save to possibly parse error dc_orf_pdrloop: lcall readSPIwait mov a,r4 cjne a,#13,dc_orf_pdr1 ;if not a CR move on ljmp dc_orf_pdrseterror ;if CR received before space then error dc_orf_pdr1: mov r1,a ;save to possibly parse error cjne a,#32,dc_orf_pdrloop ;loop until space or CR received ;next 4 bytes should be filesize mov r1,#4 mov r0,#0 ;to detect filesize 0 which indicates directory mov dptr,#readfilesize dc_orf_gfsloop: push dpl push dph lcall initSPI lcall readSPIwait pop dph pop dpl mov a,r4 movx @dptr,a inc dptr jz dc_orf_gfs1 inc r0 ;non-zero, inc r0 dc_orf_gfs1: djnz r1,dc_orf_gfsloop ;loop for 4 bytes lcall initSPI lcall readSPIwait mov a,r4 cjne a,#13,dc_orf_gfs2 ljmp dc_orf_gfs3 dc_orf_gfs2: ;cr not at end, unknown error... mov a,#usb_unkerror ljmp dc_orf_seterror dc_orf_gfs3: mov a,r0 ;get zero-size detector jnz dc_orf_setbuffer ;valid, set up buffer mov a,#usb_cferror ;call it command failed ljmp dc_orf_seterror dc_orf_setbuffer: ;file exists, file size set, set up buffer and fill with file contents clr a mov dptr,#readfileptr movx @dptr,a inc dptr movx @dptr,a inc dptr movx @dptr,a inc dptr movx @dptr,a ;file pointer cleared mov dptr,#readfilechk mov a,#'V' ;valid movx @dptr,a inc dptr mov a,#'R' ;read movx @dptr,a inc dptr mov a,#'F' ;file movx @dptr,a lcall FillReadBuffer ;initial file read ljmp hp_disk dc_readfilebyte: ;read one byte from the buffered file and return to the HP. ;if file is past EOF or not open then returns 0. mov hp_msb,#0 ;msb always 0 mov dptr,#readfilename movx a,@dptr cjne a,#13,dc_rf1 dc_rf_invalid: mov a,#usb_stierror ljmp dc_rf_error_return dc_rf_ateof: mov a,#usb_eoferror dc_rf_error_return: mov dptr,#usb_error movx @dptr,a mov hp_lsb,#0 lcall ide_2_hp ;send a 0 back to HP ljmp hp_disk dc_rf1: mov dptr,#readfilechk movx a,@dptr cjne a,#'V',dc_rf_invalid inc dptr movx a,@dptr cjne a,#'R',dc_rf_invalid inc dptr movx a,@dptr cjne a,#'F',dc_rf_invalid ;valid filename, check for eof mov dptr,#readfileptr movx a,@dptr mov dcvar1,a inc dptr movx a,@dptr mov dcvar2,a inc dptr movx a,@dptr mov dcvar3,a inc dptr movx a,@dptr mov dcvar4,a ;dcvar1-4 = lsb to msb of readfileptr mov dptr,#readfilesize movx a,@dptr cjne a,dcvar1,dc_rf_notend inc dptr movx a,@dptr cjne a,dcvar2,dc_rf_notend inc dptr movx a,@dptr cjne a,dcvar3,dc_rf_notend inc dptr movx a,@dptr cjne a,dcvar4,dc_rf_notend ljmp dc_rf_invalid ;at EOF, return 0 dc_rf_notend: ;not at end yet, get next buffer value and return to the HP, ;increment file pointer, increment buffer pointer, ;if buffer pointer at end refill buffer. Increment/refill ;can occur after returning value for some multitasking. mov dptr,#readbufptr movx a,@dptr ;lsb mov r0,a inc dptr movx a,@dptr ;msb mov r1,a mov dpl,r0 mov dph,r1 movx a,@dptr ;get buffer byte mov hp_lsb,a lcall ide_2_hp ;send byte to HP ;increment file pointer mov dptr,#readfileptr movx a,@dptr inc a movx @dptr,a jnz dc_rf_fpincdone inc dptr movx a,@dptr inc a movx @dptr,a jnz dc_rf_fpincdone inc dptr movx a,@dptr inc a movx @dptr,a jnz dc_rf_fpincdone inc dptr movx a,@dptr inc a movx @dptr,a dc_rf_fpincdone: ;increment buffer pointer mov dptr,#readbufptr movx a,@dptr inc a movx @dptr,a jnz dc_rf_bpincdone inc dptr movx a,@dptr inc a movx @dptr,a dc_rf_bpincdone: ;check to see if buffer needs filling mov dptr,#readbufptr movx a,@dptr cjne a,#readbufterm-((readbufterm/256)*256),dc_rf_exit inc dptr movx a,@dptr cjne a,#readbufterm/256,dc_rf_exit lcall FillReadBuffer dc_rf_exit: ljmp hp_disk dc_openwritefile: ;open buffered file for writing, filename[cr] must be ;sent immediately after sending command. Sending an empty filename ;(just cr) abandons the output buffer and clears the filename. ;If the file exists, data will be appended to the existing file. ;The output file is not created or appended to unless data is written. clr a mov dptr,#usb_error movx @dptr,a ;clear usb_error value mov dptr,#writefilename lcall GetFilenameFromHP mov dptr,#writefilename movx a,@dptr cjne a,#13,dc_owf_tryopen ;empty string, mark invalid and exit dc_owf_invalidexit: clr a mov dptr,#writefilechk movx @dptr,a ;mark write filename invalid lcall USBclear ;clear response buffer (if any) ljmp hp_disk dc_owf_tryopen: ;use DIR to validate name, but only check for FN, FO, ND errors ;and a return of all 0's for size which indicates a directory. ;CF error ok, just means it's a new file and doesn't exist yet. lcall SelectSCSIPH ;init/sync and select short binary commands mov r4,#0x01 ;DIR command lcall writeSPIwait mov r4,#0x20 lcall writeSPIwait mov dptr,#writefilename ;point to write filename just saved lcall SendSPIstring ;send filename + cr lcall initSPI ;back to SPI port lcall readSPIwait ;wait for response mov a,r4 cjne a,#13,dc_owf_unkerror ljmp dc_owf_parsedirresponse dc_owf_unkerror: ;cr not returned by dir... don't know why mov a,#usb_unkerror dc_owf_seterror: mov dptr,#usb_error movx @dptr,a ;set error value ljmp dc_owf_invalidexit ;invalidate filename and exit dc_owf_parsedirresponse: lcall readSPIwait mov a,r4 mov r0,a lcall readSPIwait mov a,r4 mov r1,a cjne a,#32,dc_owf_pdr1 ljmp dc_owf_pdr_gotspace dc_owf_pdr1: lcall readSPIwait mov a,r4 cjne a,#32,dc_owf_pdr2 ljmp dc_owf_pdr_gotspace dc_owf_pdr2: cjne a,#13,dc_owf_pdr1 ;loop until space or cr received ;cr received before space, either an error or file doesn't exist ;only need to check 2nd response byte (after initial cr) mov a,r1 cjne a,#'F',dc_owf_pdr3 ;is it a CF? ljmp dc_owf_fileok ;yes - filename (probably) ok dc_owf_pdr3: cjne a,#'N',dc_owf_pdr4 ;is it a FN? mov a,#usb_fnerror ;yes - invalid filename error ljmp dc_owf_seterror dc_owf_pdr4: cjne a,#'O',dc_owf_pdr5 ;is it a FO? mov a,#usb_foerror ;yes - file open error ljmp dc_owf_seterror dc_owf_pdr5: cjne a,#'D',dc_owf_unkerror ;if not ND then unknown mov a,#usb_nderror ;no disk error ljmp dc_owf_seterror dc_owf_pdr_gotspace: ;file exists, size returned - just need to make sure ;not a directory or else set the command failed error lcall readSPIwait mov a,r4 jnz dc_owf_pdr_notdir lcall readSPIwait mov a,r4 jnz dc_owf_pdr_notdir lcall readSPIwait mov a,r4 jnz dc_owf_pdr_notdir lcall readSPIwait mov a,r4 jnz dc_owf_pdr_notdir ;it's a directory lcall readSPIwait ;discard CR mov a,#usb_cferror ljmp dc_owf_seterror dc_owf_pdr_notdir: lcall USBclear ;clear remaining response dc_owf_fileok: ;set write buffer pointer to beginning of buffer... mov dptr,#writebufptr mov a,#writebuffer-((writebuffer/256)*256) movx @dptr,a inc dptr mov a,#writebuffer/256 movx @dptr,a ;clear write buffer count... clr a mov dptr,#writebufcnt movx @dptr,a inc dptr movx @dptr,a ;mark as valid read file... mov dptr,#writefilechk mov a,#'V' ;valid movx @dptr,a inc dptr mov a,#'W' ;write movx @dptr,a inc dptr mov a,#'F' ;file movx @dptr,a ;done setting up write stream ljmp hp_disk dc_writefilebyte: ;write one byte from the HP (in hp_lsb) to the buffered file. ;If the file isn't "open" then this is a no-op. mov dptr, #writefilename movx a,@dptr cjne a,#13,dc_wf_chk1 dc_wf_invalidexit: mov a,#usb_stoerror ;output filename invalid mov dptr,#usb_error movx @dptr,a ljmp dx_done ;back to disk controller dc_wf_chk1: mov dptr,#writefilechk movx a,@dptr cjne a,#'V',dc_wf_invalidexit inc dptr movx a,@dptr cjne a,#'W',dc_wf_invalidexit inc dptr movx a,@dptr cjne a,#'F',dc_wf_invalidexit ;write filename appears to be valid, add byte to buffer mov dptr,#writebufptr movx a,@dptr mov r0,a inc dptr movx a,@dptr mov dph,a mov dpl,r0 ;dptr = write buffer pointer mov a,hp_lsb movx @dptr,a ;buffer(dptr) = byte from HP ;inc write buffer pointer mov dptr,#writebufptr movx a,@dptr inc a movx @dptr,a jnz dc_wf_bpincdone inc dptr movx a,@dptr inc a movx @dptr,a dc_wf_bpincdone: ;inc write buffer count mov dptr,#writebufcnt movx a,@dptr inc a movx @dptr,a jnz dc_wf_bcincdone inc dptr movx a,@dptr inc a movx @dptr,a dc_wf_bcincdone: ;check to see if buffer needs flushing to file mov dptr,#writebufptr movx a,@dptr cjne a,#writebufterm-((writebufterm/256)*256),dc_wf_exit inc dptr movx a,@dptr cjne a,#writebufterm/256,dc_wf_exit lcall FlushWriteBuffer ;write buffer to file dc_wf_exit: ljmp dx_done dc_closewritefile: ;close buffered output file by writing the remaining buffer ;to the file (provided there is data in the output buffer), then ;marks the output file as unused. If a buffered output file isn't ;defined then this is a no-op. This clears usb_error value unless ;a flush error occurs. clr a mov dptr,#usb_error movx @dptr,a mov dptr, #writefilename movx a,@dptr cjne a,#13,dc_cf_chk1 dc_cf_exit: ljmp dx_done ;back to disk controller dc_cf_chk1: mov dptr,#writefilechk movx a,@dptr cjne a,#'V',dc_cf_exit inc dptr movx a,@dptr cjne a,#'W',dc_cf_exit inc dptr movx a,@dptr cjne a,#'F',dc_cf_exit ;write file valid, flush it lcall FlushWriteBuffer ;mark file as invalid and exit clr a mov dptr,#writefilechk movx @dptr,a ljmp dx_done dc_return_usb_error: ;this sends the usb_error value back to the HP mov hp_msb,#0 mov dptr,#usb_error movx a,@dptr mov hp_lsb,a lcall ide_2_hp ljmp hp_disk ;---------------------------------------------------------------- ; change notes... ; 10/4/08 initial HP/IDE/USB w/ debug menu save/restore/prompt/debugging ; 10/6/08 added IDE commands to read/write SPI bytes, sync and clear ; modified "hp_config" settings so portF bits 4-7 remain outputs ; 10/7/08 added DLF command to image save to delete existing image ; added extra image save size options, from 1 to 9 SFS volumes ; added code to "dos prompt" to add missing LF after CR ; 10/8/08 added calls to initSPI at controller and SPI/USB debug startup ; 10/10/08 modified sync code to avoid lockup under some conditions ; 10/12/08 added/debugged buffered streaming file read (130xxx 131xxx) ; fixed command "hook" code (136xxx 137xxx was wrong) ; changed a couple clr acc instructions to clr a ; 10/13/08 tweaked error-handling in stream read code ; added/mostly debugged stream write code ; 10/14/08 changed exits to ljmp hp_disk after HP reads/writes ; fixed open-write filename validate code ;---------------------------------------------------------------- ; ;copied and adapted from: http://www.pjrc.com/ ; ;this little program is an example of how to use the "init" type ;of startup program to initialize the baud rate and the other ;related memory locations, so that PAULMON2 will *not* attempt ;to do automatic baud rate detection, even if it configured to ;do automatic baud rate detection by default. For boards with ;flash rom, this allows the board to used for a dedicated purpose ;without having to do automatic baud rate detection every time ;the power is cycled. Startup programs other than hardware ;initialization should usually used type 253 instead of 249. ;.equ baud_const, 255 ;57600 baud w/ 11.0592 MHz ;.equ baud_const, 253 ;19200 baud w/ 11.0592 MHz ;.equ baud_const, 252 ;19200 baud w/ 14.7456 MHz ;.equ baud_const, 243 ;4808 baud w/ 12 MHz ;.equ baud_const, 244 ;9600 baud w/ 22.1182 MHz ;baud_const and autolocat defined at top of source .org autolocat .db 0xA5,0xE5,0xE0,0xA5 ;signiture bytes .db 249,255,0,0 ;id (35=prog, 249=init, 253=startup, 254=cmd) .db 0,0,0,0 ;prompt code vector .db 0,0,0,0 ;reserved .db 0,0,0,0 ;reserved .db 0,0,0,0 ;reserved .db 0,0,0,0 ;user defined .db 255,255,255,255 ;length and checksum (255=unused) .db "Fixed Baud Rate",0 .org autolocat+64 ;executable code begins here mov a, #baud_const mov 0x7B, a mov 0x7A, a ;store the baud rate for next warm boot. mov 0x79, a mov 0x78, a xrl 0x7A, #01010101b xrl 0x79, #11001100b xrl 0x78, #00011101b ret ;---------------------------------------------------------------- ;end of "HPIDEUSB" code