Uncategorised
- Details
- Geschrieben von: flockaroo
- Kategorie: Uncategorised
- Zugriffe: 914
Prototype
i had this Longer Ray5 Laser-Engraver and i always wondered how to convert it to a plotter...
>>> | ||
engraver | plotter |
using a standard servo:
...turned out that electronically there was not much conversion necessary because:
The Engraver is driven by a Makerbase board (ESP32 chip).
The laser is cntrolled by 5000 to 20000 Hz PWM signal.
in an undocumented setting ($33) the board can be configured to much lower PWM frequency (can either be done in webinterface or telnet connetion or serial or directly in the gcode-file)
$33=50
and 50Hz is the PWM frequency of standard servos, so i gave that a try...
however the laser is driven by 12 V and the servo only 5V, so iadded some DC-DC step down buck converter (voltage needs to be adjusted at the small potentiometer):
added some small board and connectors the white one is the laser-connector (12V,GND,PWM) and the black one goes to the servo (GND,5V,PWM):
its small enough to let it hang loose on the cables (for now).
and it worked... here a 1st test with a wildly mounted servo (bc i was impatient):
the servo is then driven by spindle-speed commands in G-Code (e.g. "G1 X100 Y100 S110")
info about servo:
the pwm of servo uses up to ~2200us (varies with different vendors) pulse-width (about 1/10th from full 20ms PWM cycle), so if our max spindlespeed is 1000 (preset of longer-raw5 which is 100% of PWM cycle) then only spindlespeed values up to ~110 should be used, because the servo might not be able to cope. ...i tested it and above 125 (and below ~30) the servo actually behaved weirdly and turned around all over or slowly drifted - which makes sense.
so to be safe S-values should ideally be somewhere between lets say 50 an 110 (might be different for other servos)
turned out for my servo mount the proper values were 100 just touching paper and the continuously higher pressure up until 115.
...as a followup i did some more exact calculations here:
SERVO:
servo PWM frequency 50Hz -> period = 20000 us
servo pulse widths: 500..2500 us -> 0..180 deg
Engraver RPM values (assuming 8 bit res - so ~0.4% clamped off on both ends -> 0.4%..99.6%):
r0 0 = 0.4 % duty cycle = 80 us (t0)
r1 1000 = 99.6 % duty cycle = 19920 us (t1)
values: 0..1000 -> 0.4 .. 99.6 % of 20000 -> 80us..19920us
RPM-val = r0+(t-t0)/(t1-t0)*(r1-r0)
RPM-val for 0 deg -> 500us = 0.025 * 20000 us = (500-80)/(19920-80)*1000 = 21.17
RPM-val for 180 deg -> 2500us = 0.125 * 20000 us = (2500-80)/(19920-80)*1000 = 121.98
...so RPM-values should vary from 22 to 122 for 0..180 degree
Pen Holder
Finally i modelled and 3d-printed some pen holder to make proper plots.
this pen holder has the servo connected to the pen sledge by a spring, so i can control pen-pressure continuously (...my 3d-printer is not the best of its type ;-P):
here my 2 obj files ready for 3d-printing - click image for link to obj-file (save link as...):
there's no mounting hole for the actual pen-part - i just drilled that by hand. and the spring mount was also quite improvised, just bolted it on bottom of the lower sledge and wound it through one of servo holes.
the 2 parts of the pen-sledge should be connected by two 8cm long 3mm bolts (glued on one side).
in principle one could also skip the spring and use a standard servo-saver for varying the pen-force (but i had none at hand...).
edit: ...new improved versions here for download (can also hold bigger 40mm servos):
Results
in action...
final plot...
- Details
- Geschrieben von: flockaroo
- Kategorie: Uncategorised
- Zugriffe: 394
...endless mountain scape, scrolling on and on and on
...so i found this old baby in a forgotten closet, and got carried away by some basic/assembly coding.
strange experience after long time only shader coding (assembly coding feels a bit like playing "sokoban" (for those who know) - just pushing around registers instead of boxes...)
Source Code:
for developing on pc i used ZXBasic,
just get the repos by: "git clone https://github.com/boriel/zxbasic.git"
and then simply compile my source with:
zxbc.py -taB zx_mountains.bas
Here's the source (ZXBasic + assembly) - forgive the wild coding style... most likely there's lots of room for improvement (...i haven't coded assembly for ages).
ZX_MOUNTAINS.BAS:
REM
REM ZX-Mountains by flockaroo (Florian Berger) - 2024
REM
REM License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
REM
BORDER 7
PAPER 7
INK 1
CLS
PRINT AT 21,15;" ZX-Mountains"
PRINT AT 23,23;"flockaoo"
LET rx=25
LET ry=19
REM abs(sin)
DIM asint(63) as UBYTE => {0,12,24,37,49,61,73,85,97,108,119,130,141,151,161,170,179,188,196,204,211,217,224,229,234,239,243,246,249,251,252,253,253,253,252,251,249,246,243,239,234,229,224,217,211,204,196,188,179,170,161,151,141,130,119,108,97,85,73,61,49,37,24,12}
DIM binv8(255) as UBYTE => {0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136,72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220,60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166,102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9,137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93,221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11,139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167,103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255}
function drawRightColAsm(FCx as integer)
REM PRINT "FCx0",FCx
FCx=FCx+1
ASM
push HL
pop BC
ld HL,FCx
ld (HL),C
inc HL
ld (HL),B
jp data_end
;
; div8, mul8 - from here: https://tutorials.eeems.ca/Z80ASM/part4.htm
;
div8_HL_HL_D:
push bc
Div8: ; this routine performs the operation HL=HL/D
xor a ; clearing the upper 8 bits of AHL
ld b,16 ; the length of the dividend (16 bits)
Div8Loop:
add hl,hl ; advancing a bit
rla
cp d ; checking if the divisor divides the digits chosen (in A)
jp c,Div8NextBit ; if not, advancing without subtraction
sub d ; subtracting the divisor
inc l ; and setting the next digit of the quotient
Div8NextBit:
djnz Div8Loop
pop bc
ret
mul8_HL_H_E:
push bc
ld b,d
push bc
Mul8b: ; this routine performs the operation HL=H*E
ld d,0 ; clearing D and L
ld l,d
ld b,8 ; we have 8 bits
Mul8bLoop:
add hl,hl ; advancing a bit
jp nc,Mul8bSkip ; if zero, we skip the addition (jp is used for speed)
add hl,de ; adding to the product if necessary
Mul8bSkip:
djnz Mul8bLoop
pop bc
ld d,b
pop bc
ret
mod8_HL_HL_D: ; ...derived from div8 above
push bc
ld b,e
push bc
push HL
call div8_HL_HL_D
ld H,L
ld E,D
call mul8_HL_H_E
pop BC
dec HL
ld A,H
neg
ld H,A
ld A,L
neg
ld L,A
ADD HL,BC
pop bc
ld e,b
pop bc
ret
END ASM
H_d:
asm
Hi: DEFB 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
dH: DEFB 10,10 ;=int(192/Hnum)
dC: DEFB 20 ;=int(200/Hnum)
dummy: DEFB 30 ; debug dummy
XX: DEFB 0,0
data_end:
#if 1
end asm
REM FIXME: port this to ASM!!
const Hnum=5
REM DIM H(Hnum) As UBYTE
DIM dummy(1) as UBYTE at @H_d+19
DIM dH(1) as Uinteger at @H_d+16
DIM dC(1) as UBYTE at @H_d+18
DIM H(Hnum) As UBYTE at @H_d
dH(0) = int(192/Hnum)
dC(0) = int(200/Hnum)
DIM XX as uinteger = FCx*(Hnum)
DIM Hi as uinteger
FOR i=0 to Hnum-1
Hi=(dH(0)>>2)*(i+2)+(dH(0)*i*i<<1)/Hnum/Hnum
Hi=Hi*(Hnum-i)
Hi=Hi+(asint(((XX*60 )>>8)&63)>>2)-32
Hi=Hi+(asint(((XX*137)>>8)&63)>>3)-16
if i=Hnum-1 then
Hi=Hi+(asint(((XX ) )&63)>>4)-8
end if
H(i)=Hi/(Hnum-i)
XX=XX-FCx+i*11
NEXT i
ASM
#else
; WIP... above code in ASM - not working yet... :-(
;DIM XX as uinteger = FCx*(Hnum)
push HL
ld HL,Hnum
ld B,(HL)
ld HL,0
sumloop:
ADD HL,DE
djnz sumloop
ld HL,XX ; store XX
LD (HL),E
inc HL
LD (HL),D
pop HL
;push DE ; push XX
;FOR i=0 to Hnum-1
ld B,0
loop_Hi:
;H(i)=(dH(0)>>1)+(dH(0)>>2)*i+(dH(0)*i*i<<1)/Hnum/Hnum
;approx:H(i)=(dH(0)>>2)*(i+2)+(dH(0)*i*i<<1)/Hnum/Hnum
push BC
ld HL,Hnum
ld D,(HL)
ld E,B
SLA L
ld HL,0
sumloop2:
ADD HL,DE
djnz sumloop2 ; L=i*i*2, H=Hnum*Hnum
ld D,H
ld E,L
pop BC
ld HL,dH
ld H,(HL)
push DE
call mul8_HL_H_E ; dH*i*i*2
pop DE
call div8_HL_HL_D ; HL=(dH(0)*i*i<<1)/Hnum/Hnum
push HL
push BC
ld HL,dH
ld C,(HL)
SRL C
SRL C
inc B
ld A,C
sumloop3:
add A,C
djnz sumloop3 ; A=(dH>>2)*(i+2)
pop BC
ld L,A
ld H,0
;ld HL,dH
;ld H,(HL)
;SRL H
;SRL H
;ld A,B
;add A,2
;ld E,A
;call mul8_HL_H_E
pop DE
ADD HL,DE ; HL=(dH(0)>>2)*(i+2)+(dH(0)*i*i<<1)/Hnum/Hnum
push HL
ld HL,Hnum
ld A,(HL)
SUB B
popHL
ld E,A
call mul8_HL_H_E ; HL=(...)*(Hnum-i)
push HL
;Hi=Hi+(sint(((XX*60 )>>8)&63)>>2)-32
ld HL,XX
inc HL
ld H,(HL) ; DE=XX>>8 (high byte only)
ld E,60
call mul8_HL_H_E
ld A,H
AND 63
ld HL,bsin6
ld D,0
ld E,A
add HL,DE
ld A,(HL) ;A=sint(((XX*100)>>8)&63)
SRL A
SRL A ;A=sint(((XX*100)>>8)&63)>>2
ld E,A
ld D,0
pop HL
ADD HL,DE ; +(sint(((XX*100)>>8)&63)>>2)
ld DE,0FFE0h
ADD HL,DE ; -32
push HL
;Hi=Hi/(Hnum-i)
ld HL,Hnum
ld A,(HL)
SUB B
ld D,A
pop HL
call div8_HL_HL_D ; HL=HL/(Hnum-i)
push HL
pop DE
;H(i)=Hi
ld HL,Hi
ld D,0
ld E,B
add HL,DE
ld (HL),E ; write to H(i)
;XX=XX-FCx
ld HL,XX
ld E,(HL)
inc HL
ld D,(HL) ; DE=XX
push DE
ld HL,FCx
ld E,(HL)
inc HL
ld D,(HL) ; DE=FCx
pop HL
dec DE
ld A,E
xor 255
ld E,A
ld A,D
xor 255
ld D,A
ADD HL,DE
push HL
pop DE
;ld DE, HL
ld HL,XX ; write back XX
ld (HL),E
ld (HL),D
;H(i)=H(i)+(int(sint(((XX*189)>>8)&63)-127)*12>>7)/(Hnum-i)
;if i=Hnum-1 then
; H(i)=H(i)+(int(sint(((XX*417)>>8)&63)-127)>>6)/(Hnum-i)
;end if
;NEXT i
inc B
ld A,B
ld HL,Hnum
cp (HL)
jp nz,loop_Hi
#endif
jp end_of_data
; 8 bit bit-mirrored
binv8: DEFB 0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136,72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220,60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166,102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9,137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93,221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11,139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167,103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255
; biased sin 6 bit full period 0..255
bsin6: DEFB 127,139,151,163,175,186,197,207,216,225,232,239,244,248,251,253,254,253,251,248,244,239,232,225,216,207,197,186,175,163,151,139,126,114,102,90,78,67,56,46,37,28,21,14,9,5,2,0,0,0,2,5,9,14,21,28,37,46,56,67,78,90,102,114
bayer: DEFB 0, 32, 8, 40, 2, 34, 10, 42
DEFB 48, 16, 56, 24, 50, 18, 58, 26
DEFB 12, 44, 4, 36, 14, 46, 6, 38
DEFB 60, 28, 52, 20, 62, 30, 54, 22
DEFB 3, 35, 11, 43, 1, 33, 9, 41
DEFB 51, 19, 59, 27, 49, 17, 57, 25
DEFB 15, 47, 7, 39, 13, 45, 5, 37
DEFB 63, 31, 55, 23, 61, 29, 53, 21
biX7: DEFB 0,0
FCx: DEFB 0,0
Hnum: DEFB 5
lastmin: DEFB 0
DosFont8: DEFB 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 03Ch, 042h, 0A5h, 081h, 0BDh, 042h, 03Ch, 000h, 03Ch, 07Eh, 0DBh, 0FFh, 0C3h, 07Eh, 03Ch, 000h, 000h, 0EEh, 0FEh, 0FEh, 07Ch, 038h, 010h, 000h, 010h, 038h, 07Ch, 0FEh, 07Ch, 038h, 010h, 000h, 000h, 03Ch, 018h, 0FFh, 0FFh, 008h, 018h, 000h, 010h, 038h, 07Ch, 0FEh, 0FEh, 010h, 038h, 000h, 000h, 000h, 018h, 03Ch, 018h, 000h, 000h, 000h, 0FFh, 0FFh, 0E7h, 0C3h, 0E7h, 0FFh, 0FFh, 0FFh, 000h, 03Ch, 042h, 081h, 081h, 042h, 03Ch, 000h, 0FFh, 0C3h, 0BDh, 07Eh, 07Eh, 0BDh, 0C3h, 0FFh, 01Fh, 007h, 00Dh, 07Ch, 0C6h, 0C6h, 07Ch, 000h, 000h, 07Eh, 0C3h, 0C3h, 07Eh, 018h, 07Eh, 018h, 004h, 006h, 007h, 004h, 004h, 0FCh, 0F8h, 000h, 00Ch, 00Ah, 00Dh, 00Bh, 0F9h, 0F9h, 01Fh, 01Fh, 000h, 092h, 07Ch, 044h, 0C6h, 07Ch, 092h, 000h, 000h, 000h, 060h, 078h, 07Eh, 078h, 060h, 000h, 000h, 000h, 006h, 01Eh, 07Eh, 01Eh, 006h, 000h, 018h, 07Eh, 018h, 018h, 018h, 018h, 07Eh, 018h, 066h, 066h, 066h, 066h, 066h, 000h, 066h, 000h, 0FFh, 0B6h, 076h, 036h, 036h, 036h, 036h, 000h, 07Eh, 0C1h, 0DCh, 022h, 022h, 01Fh, 083h, 07Eh, 000h, 000h, 000h, 07Eh, 07Eh, 000h, 000h, 000h, 018h, 07Eh, 018h, 018h, 07Eh, 018h, 000h, 0FFh, 018h, 07Eh, 018h, 018h, 018h, 018h, 018h, 000h, 018h, 018h, 018h, 018h, 018h, 07Eh, 018h, 000h, 000h, 004h, 006h, 0FFh, 006h, 004h, 000h, 000h, 000h, 020h, 060h, 0FFh, 060h, 020h, 000h, 000h, 000h, 000h, 000h, 0C0h, 0C0h, 0C0h, 0FFh, 000h, 000h, 024h, 066h, 0FFh, 066h, 024h, 000h, 000h, 000h, 000h, 010h, 038h, 07Ch, 0FEh, 000h, 000h, 000h, 000h, 000h, 0FEh, 07Ch, 038h, 010h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 030h, 030h, 030h, 030h, 030h, 000h, 030h, 000h, 066h, 066h, 000h, 000h, 000h, 000h, 000h, 000h, 06Ch, 06Ch, 0FEh, 06Ch, 0FEh, 06Ch, 06Ch, 000h, 010h, 07Ch, 0D2h, 07Ch, 086h, 07Ch, 010h, 000h, 0F0h, 096h, 0FCh, 018h, 03Eh, 072h, 0DEh, 000h, 030h, 048h, 030h, 078h, 0CEh, 0CCh, 078h, 000h, 00Ch, 00Ch, 018h, 000h, 000h, 000h, 000h, 000h, 010h, 060h, 0C0h, 0C0h, 0C0h, 060h, 010h, 000h, 010h, 00Ch, 006h, 006h, 006h, 00Ch, 010h, 000h, 000h, 054h, 038h, 0FEh, 038h, 054h, 000h, 000h, 000h, 018h, 018h, 07Eh, 018h, 018h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 018h, 070h, 000h, 000h, 000h, 07Eh, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 018h, 000h, 002h, 006h, 00Ch, 018h, 030h, 060h, 0C0h, 000h, 07Ch, 0CEh, 0DEh, 0F6h, 0E6h, 0E6h, 07Ch, 000h, 018h, 038h, 078h, 018h, 018h, 018h, 03Ch, 000h, 07Ch, 0C6h, 006h, 00Ch, 030h, 060h, 0FEh, 000h, 07Ch, 0C6h, 006h, 03Ch, 006h, 0C6h, 07Ch, 000h, 00Eh, 01Eh, 036h, 066h, 0FEh, 006h, 006h, 000h, 0FEh, 0C0h, 0C0h, 0FCh, 006h, 006h, 0FCh, 000h, 07Ch, 0C6h, 0C0h, 0FCh, 0C6h, 0C6h, 07Ch, 000h, 0FEh, 006h, 00Ch, 018h, 030h, 060h, 060h, 000h, 07Ch, 0C6h, 0C6h, 07Ch, 0C6h, 0C6h, 07Ch, 000h, 07Ch, 0C6h, 0C6h, 07Eh, 006h, 0C6h, 07Ch, 000h, 000h, 030h, 000h, 000h, 000h, 030h, 000h, 000h, 000h, 030h, 000h, 000h, 000h, 030h, 020h, 000h, 000h, 01Ch, 030h, 060h, 030h, 01Ch, 000h, 000h, 000h, 000h, 07Eh, 000h, 07Eh, 000h, 000h, 000h, 000h, 070h, 018h, 00Ch, 018h, 070h, 000h, 000h, 07Ch, 0C6h, 00Ch, 018h, 030h, 000h, 030h, 000h, 07Ch, 082h, 09Ah, 0AAh, 0AAh, 09Eh, 07Ch, 000h, 07Ch, 0C6h, 0C6h, 0FEh, 0C6h, 0C6h, 0C6h, 000h, 0FCh, 066h, 066h, 07Ch, 066h, 066h, 0FCh, 000h, 07Ch, 0C6h, 0C0h, 0C0h, 0C0h, 0C6h, 07Ch, 000h, 0FCh, 066h, 066h, 066h, 066h, 066h, 0FCh, 000h, 0FEh, 062h, 068h, 078h, 068h, 062h, 0FEh, 000h, 0FEh, 062h, 068h, 078h, 068h, 060h, 0F0h, 000h, 07Ch, 0C6h, 0C6h, 0C0h, 0DEh, 0C6h, 07Ch, 000h, 0C6h, 0C6h, 0C6h, 0FEh, 0C6h, 0C6h, 0C6h, 000h, 03Ch, 018h, 018h, 018h, 018h, 018h, 03Ch, 000h, 01Eh, 00Ch, 00Ch, 00Ch, 00Ch, 0CCh, 078h, 000h, 0C6h, 0CCh, 0D8h, 0F0h, 0D8h, 0CCh, 0C6h, 000h, 0F0h, 060h, 060h, 060h, 060h, 062h, 0FEh, 000h, 0C6h, 0EEh, 0FEh, 0D6h, 0C6h, 0C6h, 0C6h, 000h, 0C6h, 0E6h, 0F6h, 0DEh, 0CEh, 0C6h, 0C6h, 000h, 07Ch, 0C6h, 0C6h, 0C6h, 0C6h, 0C6h, 07Ch, 000h, 0FCh, 066h, 066h, 07Ch, 060h, 060h, 0F0h, 000h, 07Ch, 0C6h, 0C6h, 0C6h, 0C6h, 0C6h, 07Ch, 00Ch, 0FCh, 066h, 066h, 07Ch, 066h, 066h, 0E6h, 000h, 07Ch, 0C6h, 0C0h, 07Ch, 006h, 0C6h, 07Ch, 000h, 07Eh, 05Ah, 018h, 018h, 018h, 018h, 03Ch, 000h, 0C6h, 0C6h, 0C6h, 0C6h, 0C6h, 0C6h, 07Ch, 000h, 0C6h, 0C6h, 0C6h, 0C6h, 0C6h, 06Ch, 038h, 000h, 0C6h, 0C6h, 0C6h, 0C6h, 0D6h, 0EEh, 0C6h, 000h, 0C6h, 06Ch, 038h, 038h, 038h, 06Ch, 0C6h, 000h, 066h, 066h, 066h, 03Ch, 018h, 018h, 03Ch, 000h, 0FEh, 0C6h, 00Ch, 018h, 030h, 066h, 0FEh, 000h, 01Ch, 018h, 018h, 018h, 018h, 018h, 01Ch, 000h, 0C0h, 060h, 030h, 018h, 00Ch, 006h, 002h, 000h, 070h, 030h, 030h, 030h, 030h, 030h, 070h, 000h, 000h, 000h, 010h, 038h, 06Ch, 0C6h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 0FFh, 030h, 030h, 018h, 000h, 000h, 000h, 000h, 000h, 000h, 000h, 07Ch, 006h, 07Eh, 0C6h, 07Eh, 000h, 0C0h, 0C0h, 0FCh, 0C6h, 0C6h, 0C6h, 0FCh, 000h, 000h, 000h, 07Ch, 0C6h, 0C0h, 0C6h, 07Ch, 000h, 006h, 006h, 07Eh, 0C6h, 0C6h, 0C6h, 07Eh, 000h, 000h, 000h, 07Ch, 0C6h, 0FEh, 0C0h, 07Ch, 000h, 03Ch, 066h, 060h, 0F0h, 060h, 060h, 060h, 000h, 000h, 000h, 07Eh, 0C6h, 0C6h, 07Eh, 006h, 07Ch, 0C0h, 0C0h, 0FCh, 0C6h, 0C6h, 0C6h, 0C6h, 000h, 018h, 000h, 038h, 018h, 018h, 018h, 03Ch, 000h, 000h, 00Ch, 000h, 01Ch, 00Ch, 00Ch, 0CCh, 078h, 0C0h, 0C0h, 0C6h, 0D8h, 0F0h, 0D8h, 0C6h, 000h, 038h, 018h, 018h, 018h, 018h, 018h, 03Ch, 000h, 000h, 000h, 0EEh, 0FEh, 0D6h, 0C6h, 0C6h, 000h, 000h, 000h, 0FCh, 0C6h, 0C6h, 0C6h, 0C6h, 000h, 000h, 000h, 07Ch, 0C6h, 0C6h, 0C6h, 07Ch, 000h, 000h, 000h, 0FCh, 0C6h, 0C6h, 0FCh, 0C0h, 0C0h, 000h, 000h, 07Eh, 0C6h, 0C6h, 07Eh, 006h, 006h, 000h, 000h, 0DEh, 076h, 060h, 060h, 060h, 000h, 000h, 000h, 07Ch, 0C0h, 07Ch, 006h, 07Ch, 000h, 018h, 018h, 07Eh, 018h, 018h, 018h, 01Eh, 000h, 000h, 000h, 0C6h, 0C6h, 0C6h, 0C6h, 07Eh, 000h, 000h, 000h, 0C6h, 0C6h, 0C6h, 06Ch, 038h, 000h, 000h, 000h, 0C6h, 0C6h, 0D6h, 0FEh, 0C6h, 000h, 000h, 000h, 0C6h, 06Ch, 038h, 06Ch, 0C6h, 000h, 000h, 000h, 0C6h, 0C6h, 0C6h, 07Eh, 006h, 07Ch, 000h, 000h, 0FEh, 00Ch, 018h, 060h, 0FEh, 000h, 00Eh, 018h, 018h, 070h, 018h, 018h, 00Eh, 000h, 018h, 018h, 018h, 000h, 018h, 018h, 018h, 000h, 0E0h, 030h, 030h, 01Ch, 030h, 030h, 0E0h, 000h, 000h, 000h, 070h, 09Ah, 00Eh, 000h, 000h, 000h, 000h, 000h, 018h, 03Ch, 066h, 0FFh, 000h, 000h
demostr:
DEFB "flockaroo presents --=#[ ZX-Mountains ]#=-- "
DEFB " an infinite mountain scape ",127,"2024 "
DEFB "...found my old ZX-Spectrum in some forgotten closet... got inspired... and carried away... "
DEFB "cold jump into assembly waters, after long time only shader programming. "
DEFB "...for a change. "
DEFB "oh, oh, so sloooow "
DEFB "...now some garbage: "
; use dos font or spectrum ROM-font
#define MYFNT 3C00h
;#define MYFNT DosFont8
end_of_data:
; FCx - get from mem
ld HL,FCx
ld C,(HL)
inc HL
ld B,(HL)
push BC
;use ordered halftone (bayer) or my own bitinv noise
#define USE_BAYER
#ifndef USE_BAYER
;DIM biX7 as integer=7*binv8(FCx&255)
pop HL ; get BC from above (FCx)
ld C,L
ld B,0
ld HL,binv8
ADD HL,BC
;ld L,(HL)
ld E,(HL)
ld H,7
push BC
CALL mul8_HL_H_E
pop BC
push HL
pop BC
ld HL,biX7
ld (HL),C
inc HL
ld (HL),B
#endif
; uncomment to step from nearest to farest mountsins (default far to near)
;#define Hi_DOWN_LOOP
#ifndef Hi_DOWN_LOOP
ld HL,lastmin ; optim: init last actual mountain
ld (HL),0 ; optim: init last actual mountain
#else
push DE
ld HL,Hnum
ld D,(HL)
dec D
ld HL,lastmin ; optim: init last actual mountain
ld (HL),D ; optim: init last actual mountain
pop DE
#endif
; screen addr = 0x401F (rightmost byte on top)
ld HL,401Fh
; FOR FCy=0 to 192
ld C,0
forFCy:
ld A,C
cp 192
jp z,forFCyEnd
push BC
push HL ; push addr
#ifndef USE_BAYER
;DIM pat as ubyte=int(73*binv8(FCy)+biX7) MOD 255
ld HL,binv8
ld B,0
ADD HL,BC
;ld L,(HL)
ld E,(HL)
;ld H,73
ld H,3
push BC
CALL mul8_HL_H_E
pop BC
push HL
ld HL,biX7
ld C,(HL)
inc HL
ld B,(HL)
pop HL
ADD HL,BC
ld D,255
push BC
CALL mod8_HL_HL_D
pop BC
ld E,L ; E=pat
#else
ld HL,FCx
ld A,(HL)
ld E,A
and 7
SLA A
SLA A
SLA A
ld HL,bayer
push DE
ld D,0
ld E,A
add HL,DE
pop DE
push BC
ld B,0
ld A,C
and 7
ld C,A
ADD HL,BC
pop BC
ld E,(HL)
SLA E
SLA E
#endif
pop HL ; pop addr
pop BC
push DE ; push pat
push BC ; push bright,FCy
push HL ; push addr
ld A,235
ld B,C
SLA B
SLA B
SUB B
ld B,A
; FOR i=0 to Hnum-1 (...or FOR i=Hnum-1 to 0)
ld HL,lastmin ; optim: load last actual mountain
ld D,(HL) ; optim: load last actual mountain
for_i:
;if(FCy>H(i))
push DE
push BC
ld A,C
ld HL,Hi
ld E,D ; push D
ld D,0
add HL,DE
ld D,E ; pop D
ld E,(HL) ; E now holds H(i) (where i=D)
cp E
jp c,label1 ; cflag set when FCy<Hi
;B=127-(i+1)*(dC>>1)+(FCy-H(i))*2*Hnum/(i+1)
#ifndef Hi_DOWN_LOOP
ld HL,lastmin ; optim: store last actual mountain
ld (HL),D ; optim: store last actual mountain
#endif
push DE
ld E,D
inc E
ld HL,dC
ld H,(HL)
SRL H
CALL mul8_HL_H_E ; HL=(i+1)*(dC>>1)
pop DE
push HL
ld A,C
SUB E ; A=FCy-Hi
ld HL,Hnum
ld E,(HL)
ld H,A
;push AF
;SLA E ; E=Hnum*2
ld A,E
SRL E
add A,E ; Hnum*1.5
ld E,A
;pop AF
CALL mul8_HL_H_E ; HL=(Fc-Hi)*2*Hnum
push DE
inc D
CALL div8_HL_HL_D ; HL=(Fc-Hi)*2*Hnum/(i+1)
pop DE
ld A,L
ADD A,100 ; A=127+(Fc-Hi)*2*Hnum/(i+1)
pop BC ; BC=(i+1)*(dC>>1)
sub C
ld B,A ; B=127-(i+1)*(dC>>1)+(FCy-H(i))*2*Hnum/(i+1)
#ifdef Hi_DOWN_LOOP
ld A,B ; preserve B(=brightness)
pop BC
ld B,A ; preserve B(=brightness)
pop DE
jp breakHi
#endif
label1:
ld A,B ; preserve B(=brightness)
pop BC
ld B,A ; preserve B(=brightness)
pop DE
#ifndef Hi_DOWN_LOOP
inc D
ld HL,Hnum
ld A,(HL)
cp D
jp nz,for_i
#else
dec D
ld A,255
cp D
jp nz,for_i
breakHi:
#endif
pop HL ; HL=addr
ld A,B ; preserve B=bright
pop BC ; pop FCy (C)
ld B,A ; preserve B=bright
pop DE ; pop pat (E)
; add text banner on bottom
#if 1
push BC
push DE
push HL
ld HL,bsin6
ld DE,(FCx)
ld A,E
SRL A
SRL A
AND 63
ld E,A
ld D,0
ld A,C
add HL,DE
ld E,(HL)
SRL E
SRL E
SRL E
SRL E
add A,E
ld C,A
pop HL
pop DE
ld A,159
CP C
jp nc,notext:
ld A,175
CP C
jp c,notext:
SRL B
;SLA B
; add scrolltext to B
push HL
push DE
push BC
;D=FCx/2&7
ld A,(FCx)
SRL A
AND 7
ld D,A
PUSH DE
;E=str(FCx/16)
ld DE,(FCx)
SRL D
jp nc,Xshift1_jp1
SRL E
SET 7,E
jp Xshift1_jp2
Xshift1_jp1:
SRL E
Xshift1_jp2:
SRL D
jp nc,Xshift2_jp1
SRL E
SET 7,E
jp Xshift2_jp2
Xshift2_jp1:
SRL E
Xshift2_jp2:
SRL D
jp nc,Xshift3_jp1
SRL E
SET 7,E
jp Xshift3_jp2
Xshift3_jp1:
SRL E
Xshift3_jp2:
SRL D
jp nc,Xshift4_jp1
SRL E
SET 7,E
jp Xshift4_jp2
Xshift4_jp1:
SRL E
Xshift4_jp2:
ld HL,demostr
ADD HL,DE
ld A,(HL) ; A=char
;ld HL,Font8+D*8+C/2
; 16 bit SLA
ld D,0
SLA A
jp nc,shift1_jp1
SLA D
SET 0,D
jp shift1_jp2
shift1_jp1:
SLA D
shift1_jp2:
SLA A
jp nc,shift2_jp1
SLA D
SET 0,D
jp shift2_jp2
shift2_jp1:
SLA D
shift2_jp2:
SLA A
jp nc,shift3_jp1
SLA D
SET 0,D
jp shift3_jp2
shift3_jp1:
SLA D
shift3_jp2:
ld E,A ; DE=char*8
;ld HL,Font8
ld HL,MYFNT
ADD HL,DE
LD A,C
SRL A
AND 7
ld E,A ; E=(FCy/2)%8
ld D,0
ADD HL,DE
POP DE
push BC
ld B,(HL)
ld A,D ; A=(FCx/2)%8
fontjmp1:
cp 0
jp z,fontjmp2
SLA B
dec A
jp fontjmp1
fontjmp2:
ld A,B
pop BC
BIT 7,A
jp z,nochar
ld A,167
ADD A,B
ld B,A
;ld B,255 ; fully white text - no halftone
nochar:
LD A,B
pop BC
LD B,A
pop DE
pop HL
notext:
ld A,B
pop BC
ld B,A
#endif
;if pat>br THEN POKE addr, 255 else POKE addr, 0
;ld (HL),255
SET 0,(HL)
ld A,B
cp E
jp c,label2
;ld (HL),0
RES 0,(HL)
label2:
; addr=addr+256
inc H
;if (FCy & 7 = 7) THEN addr=addr-2016
ld A,C
and 7
cp 7
jp nz,label3
;SUB HL,2016
push BC
ld BC,0F820h
ADD HL,BC
pop BC
label3:
ld A,C
;if (FCy & 63 = 63) THEN addr=addr+1792
and 63
cp 63
jp nz,label4
push BC
ld BC,1792
ADD HL,BC
pop BC
label4:
inc C
jp forFCy
forFCyEnd:
END ASM
end function
function fastcall scrollX(x as ubyte) as ubyte
REM arg x ignored for now, just scroll 1 pixel
ASM
LD HL,4000h
SLA (HL)
inc HL
loop0:
SLA (HL)
JP nc,jmp1
DEC HL
SET 0,(HL)
INC HL
jmp1:
inc HL
LD A,H
CP 58h
JP nz,loop0
END ASM
end function
DIM FCx as uinteger=0
WHILE 1
drawRightColAsm(FCx)
scrollX(1)
FCx=FCx+1
END WHILE
- Details
- Geschrieben von: flockaroo
- Kategorie: Uncategorised
- Zugriffe: 967
ssd crash - or, how i almost lost my whole ext4-home-drive but got almost all data back
...one fine day, i had a really bad day...
...so here's the report from my odyssey:
...what happened (most likely) was, that my ssd got too little power for a while and some data got (permanently) lost.
i transitionally (and stupidly) used an old power supply in an old case, that was most likely under-dimensioned, and my GeForce-3070 drew too much power - leaving the disks slightly under-supplied (at least that's my suspicion on what happened). it worked quite well for a while, but one day during a more intense graphics session the screen suddenly froze and the system would not be able to mount my home disk on reboot. no way to mount the drive anymore, there was a lot of input/output errors... i thought i was out of luck...
what i found on internet/forums was, that if there's input/output-errors, you can assume that the disk is dead or dying, no way to restore data whatsoever...
i didn't dare to fsck because i didn't want to risk any writing to the damaged disk (not even sure if fsck wouldn't have worked properly at all on input/output errors)
...so, here's what i did:
first i tried to "dd" the whole disk to a file, but every time an input /output error occured, dd would of course stop.
but the i/o errors were distinctly bound to certain blocks, so i just wrote a small script that would just report the bad blocks into a log file and with every new start continue on the next potentially undamaged block - you cannot use this script just like this, it's more of explanatory use, because a lot is hardcoded here e.g. the drive '/dev/sdd'... - so i put this script here without warranty - just use/adapt it at your own risk, and only if you know what you are doing!!
#!/bin/sh
BAKFILE="bak_sdd_all.raw"
BSIZE=512
ALLCNT=0
if test -e $BAKFILE; then
ALLCNT=$(du -b $BAKFILE)
ALLCNT=${ALLCNT%%$BAKFILE}
#echo "allcnt= $ALLCNT"
ALLCNT=$[ALLCNT/BSIZE+1]
echo "proceed at: $ALLCNT"
fi
ALLBLOCKS=3907029168
NUMBLOCKS=$[ALLBLOCKS-ALLCNT]
echo " ...try copy $NUMBLOCKS blocks"
if [ "$NUMBLOCKS" -le "0" ]; then exit 1; fi
set +e
dd if=/dev/sdd of=$BAKFILE bs=$BSIZE skip=$ALLCNT seek=$ALLCNT count=$NUMBLOCKS > make_bak_last.out 2>&1
set -e
LASTCNT=$(grep -o '^.*records in' make_bak_last.out)
LASTCNT=${LASTCNT%%+*}
ALLCNT=$[ALLCNT+LASTCNT]
echo "$ALLCNT - $LASTCNT" | tee -a "$BAKFILE.log"
...and that script i then executed in an endless loop until all data was copied:
#!/bin/sh
while true; do
if ! ./make_bak_sdd ; then
exit 1;
fi
done
(edit: from a user comment, i got the hint, that the option "noerror" of dd can also copy all data without interrupting - ...of course i would not get a log file then)
the copying took quite a while, because every bad block would block the whole process for several seconds, and even some of the intact blocks seemed to copy slower than others.
first i just browsed through the raw data to find some source files i lost by just string-searching the whole 2TB for certain strings, i knew would only occur in my files. that's where i already got lucky and got back a few files i would have deeply missed instead. but that method is only useful for readable files, where you actually have a distinct idea of parts of the content.
as stated before, in the .log file of the above script i already got all the dead blocks listed, because every time dd interrupted, a line was added to the log.
this way i was able to make a small pyhton script, that showed me all the bad blocks (...or at least regions with bad blocks). and furthermore i extended the program to even be able to hex-view directly by pointing with the mouse to a region and also added a nifty (kind of performant) string search feature. ...might be handy for others so i also add the source here, too. i also give this here to the public without any warranty. use at your own risk, and only use it if you know what you're doing.
the script uses tkinter as a gui, and also a lot here is hardcoded. e.g it assumes a 2TB disk (deviceblocks=3907029168 (each 512bytes)).
it takes the log file (with the bad blocks) as an argument and reads the actual data (for displaying hex-data info) from the accordingly named raw-data file (without the ".log" suffix in the end - also here simply hardcoded by stripping the suffix)
#!/usr/bin/python3
import os, sys, math, mmap, tkinter
window = tkinter.Tk()
deviceblocks=3907029168;
blockspp=4096;
startblock=0
psiz=2
#W=1792
#H=1066
#W=1024
#H=1024
W=1384
H=1384
if len(sys.argv)<2 : print("usage: bla file [blockspp [startblock [psiz [W [H]]]]]"); exit(1);
fname=sys.argv[1]
if len(sys.argv)>2 : blockspp=int(sys.argv[2],0)
if len(sys.argv)>3 : startblock=int(sys.argv[3],0)
if len(sys.argv)>4 : psiz=int(sys.argv[4],0)
if len(sys.argv)>5 : W=int(sys.argv[5],0)
if len(sys.argv)>6 : H=int(sys.argv[6],0)
Nall=int(W/psiz)*int(H/psiz)
Ndevice=int((deviceblocks+blockspp-1)/blockspp)
#window.geometry(""+str(W)+"x"+str(H))
window.geometry("%dx%d"%(W,H))
canv = tkinter.Canvas(window, width=W, height=H)
ring=bytearray(256)
ringi=0
actblock=startblock
acterrs=0
actidx=0
errcnt=0
errs=[]
with open(fname, mode="r") as file:
for line in file:
vals=line.rstrip().split(" - ")
errblock=int(vals[0])
blocksbefore=int(vals[1])
while errblock>=actblock+blockspp:
errs.append(acterrs)
errcnt+=acterrs
if acterrs!=0 : print(acterrs)
acterrs=0
actblock+=blockspp
if len(errs)>=Nall: break;
acterrs+=1
idx=0
for y in range(0,H,psiz):
for x in range(0,W,psiz):
r=int(x/psiz);
g=int(y/psiz);
b=0;
r=255;
g=128;
b=0;
if idx<len(errs):
v01=0
if errs[idx]>0 :
v01=max(0.,min(1.,errs[idx]/blockspp))
v01=math.pow(v01,.5)
r=int(v01*255.999)
g=int((1.-v01)*255.999)
b=0
else :
r=g=b=255
if idx>Ndevice : [r,g,b]=[0,0,255]
idx+=1
col='#'+('%02x'%r)+('%02x'%g)+('%02x'%b)
canv.create_rectangle(x, y, x+psiz, y+psiz, width=0, fill=col)
numbytesFull=Nall*blockspp*512
numbytes=(actblock-startblock+1)*512
txt=""
txt+='%d / (%d) bytes'%(numbytes,numbytesFull)
txt+="\n\n"
txt+="%.3f MB"%(numbytes/1024/1024)
txt+="\n\n"
txt+="%.3f GB"%(numbytes/1024/1024/1024)
txt+="\n\n"
txt+='%.3f MB = %.6f %% damaged'%(errcnt*512/1024/1024,errcnt/(actblock-startblock+1)*100.0)
sumtext=canv.create_text(W/2, H/2, text=txt)
hexview=None
hexoffs=0
hexhex=0
def entryCb(v):
global hexview, hexoffs
hexoffs=int(v.get())
pos=canv.coords(hexview)
updateHexView(pos[0],pos[1],hexoffs);
entryVar = tkinter.StringVar()
entryVar.trace("w", lambda name, index, mode, entryVar=entryVar: entryCb(entryVar))
searchEntryVar = tkinter.StringVar()
def myread(fname, offs, size) :
with open(fname, mode="rb") as file:
file.seek(offs)
return file.read(size)
def readableCh(ch) :
if ch>=32 and ch<=126: return ch
return ord('.')
def bytes2hex(data,xnum,dohex) :
txt=""
line=""
line2=""
for i in range(len(data)):
line+="%02x "%data[i]
line2+="%c"%readableCh(data[i])
if (i%xnum)==xnum-1 :
ll=line2
if dohex==1: ll=line+" "+line2
txt+=ll+"\n"
line=""
line2=""
return txt
def updateHexView(x,y,offs):
global sumtext, hexview, hexhex
lnum=48
xnum=32
if hexhex==0: xnum=128
cdata=myread(fname.replace(".log",""),offs,xnum*lnum);
txt=("offs=%d\n"%offs)+bytes2hex(cdata,xnum,hexhex);
print("offs=%d"%offs)
if hexview != None : canv.delete(hexview)
if sumtext != None :
canv.delete(sumtext)
sumtext=None
#hexview=canv.create_text(x,y,text=txt,font='TkFixedFont')
hexview=canv.create_text(x,y,text=txt,font=('Mono',6))
bbox=canv.bbox(hexview);
if bbox[0]<0 : canv.move(hexview,-bbox[0],0)
if bbox[2]>W : canv.move(hexview,-bbox[2]+W,0)
if bbox[1]<0 : canv.move(hexview,0,-bbox[1])
if bbox[3]>H : canv.move(hexview,0,-bbox[3]+H)
canv.pack()
def updateHexView0():
global hexview, hexoffs
pos=canv.coords(hexview)
updateHexView(pos[0],pos[1],hexoffs);
def lclick(event):
global hexview, hexoffs
x, y = event.x, event.y
hexoffs=(int(y/psiz)*int(W/psiz)+int(x/psiz))*blockspp*512
updateHexView(x,y,hexoffs)
def prevPage(e):
global hexview, hexoffs, hexhex
lnum=48
xnum=32
if hexhex==0: xnum=128
hexoffs-=lnum*xnum
updateHexView0()
def nextPage(e):
global hexview, hexoffs, hexhex
lnum=48
xnum=32
if hexhex==0: xnum=128
hexoffs+=lnum*xnum
updateHexView0()
def prevLine(e):
global hexview, hexoffs, hexhex
lnum=48
xnum=32
if hexhex==0: xnum=128
hexoffs-=xnum
updateHexView0()
def nextLine(e):
global hexview, hexoffs, hexhex
lnum=48
xnum=32
if hexhex==0: xnum=128
hexoffs+=xnum
updateHexView0()
def checkRing(b1):
global ring, ringi
lr=len(ring)
lb=len(b1)
ri=(ringi+lr+lr-lb)%lr
for i in range(lb) :
if b1[i]!=ring[ri] : return 0
ri=(ri+1)%lb
return 1
def searchStr():
### FIXME: optimize me
global hexoffs, fname, ring, ringi
with open(fname.replace(".log",""), mode="rb") as file:
file.seek(hexoffs)
rpos=hexoffs
lr=len(ring)
b1=bytes(searchEntryVar.get(),'UTF-8')
while 1:
ch = file.read(1)
if not ch: print("not found"); break;
ring[ringi] = ch[0];
ringi=(ringi+1)%lr
if(checkRing(b1)):
print("found at pos "+str(rpos))
hexoffs=rpos
updateHexView0()
break;
rpos+=1
if((rpos-hexoffs)%10000000==0) : print("search offs = "+str(rpos-hexoffs))
def searchStrMM():
global hexoffs, fname, ring, ringi
offs=hexoffs
bsize=1024*1024*128
b1=bytes(searchEntryVar.get(),'UTF-8')
## do e really need os. here?
myfname=fname.replace(".log","")
fsize = os.path.getsize(myfname)
with open(myfname, mode="rb") as file:
while 1:
offs1=offs%mmap.ALLOCATIONGRANULARITY
offs0=offs-offs1
if offs>=fsize : break
if offs0+bsize>fsize : bsize=fsize-offs0
s = mmap.mmap(file.fileno(), length=bsize, access=mmap.ACCESS_READ, offset=offs0)
res = s.find(b1,offs1)
if res != -1:
print("found at pos "+str(offs0+res))
entryVar.set(offs0+res)
#hexoffs=offs0+res
#updateHexView0()
return
else:
offs+=bsize-len(b1)+1
print("search off=%d (%.2fG)"%(offs,offs/(1024*1024*1024)))
print("not found")
return
def openOffsetPopup(e):
global entryVar
top=tkinter.Toplevel(window)
top.geometry("320x200")
top.title("Offset Control")
#tkinter.Label(top, text= "Enter Offset!", font=('Mistral 12 bold')).place(x=0,y=40)
tkinter.Label(top, text= "Enter Offset:").place(x=10,y=10)
entryVar.set(str(hexoffs))
tkinter.Entry(top,textvariable=entryVar).place(x=20,y=30)
tkinter.Entry(top,textvariable=searchEntryVar).place(x=20,y=50)
tkinter.Button(top, text="search", command=searchStrMM).place(x=20,y=70)
def toggleHex(e):
global hexhex
if hexhex==0 : hexhex=1
else : hexhex=0
updateHexView0()
#window.bind('<Motion>', lclick)
window.bind('<B1-Motion>', lclick)
window.bind('<Button-1>', lclick)
window.bind('<Prior>', prevPage)
window.bind('<Next>', nextPage)
window.bind('<Up>', prevLine)
window.bind('<Down>', nextLine)
window.bind('o', openOffsetPopup)
window.bind('h', toggleHex)
canv.pack()
window.mainloop()
here is the final view of my log file in the python script - each pixel (or actually 2x2 pixels) holds 8192 blocks (can be given as argument) of 512 bytes each. the green/dark pixels mark damaged blocks (bright green to red for little to many damaged blocks). the blue region in the end is just the region beyond the actual ssd-capacity (as stated - here hardcoded). (also check the window.bind(...) commands in the end of the script for key-functions)
and another one with some hexing-action by just clicking anywhere on the data-region (...here obviously some excerpt from the face-section of a wavefront/obj file):
happy end (hopefully)
but hey, now for the best part... it turned out, in the end the whole raw-data browsing by hand was actually more or less just for the fun of it...
the big raw-data file i got in the end i simply dd'ed to a new equivalent ssd-drive and it mounted like charm (even without fsck) - of course some file content or maybe even whole files/directories might still be lost, but so far i didn't notice anything missing :-) ...didn't try to do a fsck on the new disk so far - for now i just mount it purely readonly and copy back old stuff to my new home on demand when i need it.
conclusion:
...don't throw away your disk just because the system doesn't mount it anymore!! make an image to a file/or even another disk, by just ignoring bad blocks. it might work - it did for me!
...and don't use too weak power supplies it might kill your ssd-drives (...but that's just my suspicion here)
as a matter of fact i'm quite disappointed, that the file system (here ext4) would just go bonkers and not let me see any files at all. i could have lived with it being slow, when stumbling over bad blocks, but just to give input/output errors and bailing out and not mounting (even when all needed information is obviously still on the disk) is not nice - ...son i'm disappoint :-(