Jump to content
IGNORED

Annotated Lynx boot rom


LX.NET

Recommended Posts

As promised, here is my annotation of the Lynx boot rom. All comments to improve this are welcome.

I noticed that the boot.src (source code to first decrypted frame) has some internal zp variable names. I will factor those in and improve the disassembly. That's for tomorrow, because it is getting too late now.

 

Let me know what you guys think.

 

; Fill shift register for cartridge

FE00 38 SEC

FE01 80 0A BRA $FE0D

FE03 90 04 BCC $FE09

FE05 8E 8B FD STX $FD8B ; Set cartridge output (bit to shift into register) to X

FE08 18 CLC

FE09 E8 INX ;

FE0A 8E 87 FD STX $FD87 ;

FE0D A2 02 LDX #$02

FE0F 8E 87 FD STX $FD87 ; SYSCTL1: Power on and strobe

FE12 2A ROL A

FE13 9C 8B FD STZ $FD8B ; Zero for bit to shift into register

FE16 D0 EB BNE $FE03

FE18 60 RTS

; Clear all memory from $0003 to $FFFF to 0x00

FE19 64 01 STZ $01

FE1B A9 00 LDA #$00

FE1D 91 00 STA ($00),y

FE1F C8 INY

FE20 D0 FB BNE $FE1D

FE22 E6 01 INC $01

FE24 D0 F7 BNE $FE1D

; Routine to initialize Mikey

FE26 A2 0D LDX #$0D

FE28 BD D8 FF LDA $FFD8,x

FE2B BC CC FF LDY $FFCC,x

FE2E 99 00 FD STA $FD00,y

FE31 CA DEX

FE32 D0 F4 BNE $FE28

; Copy 256 bytes from $FEC1 to $5000

FE34 BD C1 FE LDA $FEC1,x

FE37 9D 00 50 STA $5000,x

FE3A E8 INX

FE3B D0 F7 BNE $FE34

; Start decryption process for first frame

FE3D 64 05 STZ $05 ; Destination address of decrypted data (low)

FE3F A9 02 LDA #$02

FE41 85 06 STA $06 ; Destination address of decrypted data (high)

FE43 64 02 STZ $02 ; Initialize transition byte

FE45 A9 00 LDA #$00

FE47 20 00 FE JSR #FE00 ; Call SetCartBlock routine

; Load RSA

FE4A AD B2 FC LDA $FCB2 ; Read cart (using strobe CART0)

FE4D C9 FB CMP #$FB ; First byte has two's complement of number of blocks in first frame

FE4F 90 4C BCC $FE9D ; If value is less than #$FB it is not a correct header

FE51 85 07 STA $07 ; Save block count

FE53 9C AF FD STZ $FDAF ; GREENF = 0

FE56 9C BF FD STZ $FDBF ; BLUEREDF = 0

; Read entire block of encrypted data (reversed)

FE59 A2 32 LDX #$32 ; Each block in a frame is 50 + 1 byte long

FE5B AD B2 FC LDA $FCB2 ; Read cart (using strobe CART0)

FE5E 95 AA STA $AA,x ; Store block data in ZP from $AA to $DC

FE60 CA DEX

FE61 10 F8 BPL $FE5B ; Next byte

; Decrypt current block

FE63 05 AB ORA $AB ; Accumulator contains last byte of encrypted data

FE65 05 AC ORA $AC

FE67 F0 34 BEQ $FE9D ; First three bytes should contain at least one non-zero value, otherwise error

FE69 A2 02 LDX #$02 ; Start at position 2

FE6B B5 AA LDA $AA,x ; Read value

FE6D FD 9A FF SBC $FF9A,x ; Subtract with public part of encryption key

FE70 CA DEX

FE71 10 F8 BPL $FE6B ; Next value

FE73 B0 28 BCS $FE9D ; If sanity check fails go to error

FE75 20 00 50 JSR $5000 ; Start decryption (plaintext = encrypted ^ public_exponent % modulus)

FE78 B2 0B LDA ($0B) ; Sanity check for contents of decrypted (and still obfuscated) data's first byte

FE7A C9 15 CMP #$15 ; Must always be $15 (do not know why)

FE7C D0 1F BNE $FE9D ; On error

FE7E A5 02 LDA $02 ; Get transition byte between blocks

FE80 A0 32 LDY #$32 ; Start at end of decrypted data

FE82 18 CLC

FE83 71 0B ADC ($0B),y ; A += decrypted data

FE85 92 05 STA ($05) ; Store at destination address ($0200) and onwards

FE87 E6 05 INC $05 ; Update destination

FE89 88 DEY ; Next byte

FE8A D0 F6 BNE $FE82 ; Until all data in block is done

FE8C 85 02 STA $02 ; Remember last byte

FE8E E6 07 INC $07 ; Next block

FE90 D0 C7 BNE $FE59 ; Start reading data from cart

FE92 A0 02 LDY #$02

FE94 8C 8B FD STY $FD8B ; Set cart address data (IODAT)

FE97 AA TAX

FE98 D0 03 BNE $FE9D ; On error retry

FE9A 4C 00 02 JMP $0200 ; Run whatever is at $0200

; Retry lots of times

FE9D E6 03 INC $03

FE9F D0 09 BNE $FEAA

FEA1 E6 04 INC $04

FEA3 D0 05 BNE $FEAA

FEA5 9C 87 FD STZ $FD87 ; Reset cart address counter

FEA8 80 FE BRA $FEA8 ; Give up and loop forever

; Initialize Suzy

FEAA A2 08 LDX #$08

FEAC BD EF FF LDA $FFEF,x

FEAF BC E6 FF LDY $FFE6,x

FEB2 99 00 FC STA $FC00,y

FEB5 CA DEX

FEB6 10 F4 BPL $FEAC

FEB8 9C 91 FD STZ $FD91 ; Reset CPU bus request flip flop (draw INSERT GAME sprite)

FEBB 9C 90 FD STZ $FD90 ; Clear SDONEACK

FEBE 4C 26 FE JMP $FE26 ; Call routine to initialize Mikey

; Start of 256 byte area that is copied to $5000

FEC1 A9 11 LDA #$11

FEC3 85 0B STA $0B ; Pointer to start of zero page temporary data

FEC5 A9 44 LDA #$44 ; End of working data zero page address

FEC7 8D 71 50 STA $5071 ; Self-modifying code (see FF32)

FECA A9 AA LDA #$AA

FECC 85 0F STA $0F ; Pointer to start of encrypted data in zero page range

FECE 20 18 50 JSR $5018 ; Currently FED9: perform first multiplication of enc * enc * enc (encrypted^3)

FED1 A5 0B LDA $0B ; Transfer pointer to data with enc^2 ...

FED3 85 0F STA $0F ; ... to encryption data

FED5 A9 77 LDA #$77 ; Set pointer for work data right after squared encryption data

FED7 85 0B STA $0B ; Perform second multiplication of enc * enc * enc (encrypted^3)

; Clear all 50 values of temporary work data

FED9 A0 32 LDY #$32

FEDB A9 00 LDA #$00

FEDD 91 0B STA ($0B),y

FEDF 88 DEY

FEE0 10 FB BPL $FEDD

; Montgomery multiplication algorithm

FEE2 C8 INY ; Y = 0

FEE3 B1 0F LDA ($0F),y ; Load value from encrypted data (or enc^2 on second pass)

FEE5 85 0A STA $0A ; Store in work variable

FEE7 C6 08 DEC $08 ;

FEE9 A5 0B LDA $0B ; Change data address to work on

FEEB 8D 37 50 STA $5037 ; Self-modifying code (see FEF7)

FEEE 8D 43 50 STA $5043 ; Self-modifying code (see FF04)

FEF1 8D 47 50 STA $5047 ; Self-modifying code (see FF08)

FEF4 18 CLC

FEF5 A2 32 LDX #$32

FEF7 36 00 ROL $00,x ; Operand corresponds to $5037

FEF9 CA DEX

FEFA 10 FB BPL $FEF7

FEFC 06 0A ASL $0A

FEFE 90 11 BCC $FF11

FF00 A2 32 LDX #$32

FF02 18 CLC

FF03 B5 00 LDA $00,x ; Operand corresponds to $5043

FF05 75 AA ADC $AA,x

FF07 95 00 STA $00,x ; Operand corresponds to $5047

FF09 CA DEX

FF0A 10 F7 BPL $FF03

FF0C 20 5D 50 JSR $505D ; Call routine X

FF0F 90 03 BCC $FF14

FF11 20 5D 50 JSR $505D ; Call routine X

FF14 46 08 LSR $08

FF16 D0 D1 BNE $FEE9

FF18 C8 INY

FF19 C0 33 CPY #$33

FF1B 90 C6 BCC $FEE3

FF1D 60 RTS ; $11 to $76 contains enc^2 at first pass

; Routine X (unknown purpose: Step in montgomery multiplication process?)

FF1E A5 0B LDA $0B

FF20 8D 6C 50 STA $506C

FF23 B2 0B LDA ($0B)

FF25 CD 9A FF CMP $FF9A ; Start of public part of encryption key

FF28 90 18 BCC $FF42 ; Done

FF2A A2 32 LDX #$32

FF2C B5 00 LDA $00,x

FF2E FD 9A FF SBC $FF9A,x ; Subtract from corresponding byte in public key

FF31 95 00 STA $00,x ; Operand corresponds to $5071 when copied

FF33 CA DEX

FF34 10 F6 BPL $FF2C

FF36 90 0A BCC $FF42 ; Done

FF38 A5 0B LDA $0B

FF3A AE 71 50 LDX $5071

FF3D 86 0B STX $0B ; Update pointer to work data

FF3F 8D 71 50 STA $5071 ; Self-modify code

FF42 60 RTS

; SCB data for INSERT GAME sprite

FF43 05 ; SPRCTLO (1 bit/pixel, no flipping, non-collideable sprite)

FF44 93 ; SPRCTL1 (Totally literal, HSIZE and VSIZE specified, drawing starts at upper left quadrant)

FF45 00 ; SPRCOLL

FF46 00 00 .dw $0000 ; Address of next SCB

FF48 92 50 .dw $5092 ; Address of SPRDLINE (sprite data)

FF4A 80 00 .dw $0080 ; HPOSSTRT

FF4C 48 00 .dw $0048 ; VPOSSTRT

FF4E 00 04 .dw $0400 ; HSIZE (magnify by 4 horizontally)

FF50 00 04 .dw $0400 ; VSIZE (magnify by 4 vertically)

; Palette colors

FF52 F0 04 E2 EA 87 04 FA AA

; Sprite data (INSERT GAME upside down)

FF53 04 E2 EA 87 ; ...000.0...0.0.0.0000...

FF57 04 FA AA B7 ; .....0.0.0.0.0.0.0..0...

FF5B 04 F2 08 97 ; ....00.00000.000.00.0...

FF5F 04 FA 4A F7 ; .....0.00.00.0.0....0...

FF63 04 E2 E8 87 ; ...000.0...0.000.0000...

FF67 02 FF ; ........

FF69 05 B5 11 68 FF ; .0..0.0.000.000.0..0.000 .... ....

FF6E 04 B5 D7 2D ; .0..0.0...0.0...00.0..0.

FF72 04 B9 91 0D ; .0...00..00.000.0000..0.

FF76 04 B5 DD 4D ; .0..0.0...0...0.0.00..0.

FF7A 05 19 11 68 FF ; 000..00.000.000.0..0.000........

FF7F 00 .db $00 ; End of quadrant

; Start of boot sequence

FF80 AD 88 FC LDA $FC88 ; Load SUZYHREV hardware version (always 1.0 for hardware)

FF83 F0 7B BEQ $0000

FF85 68 PLA

FF86 68 PLA

FF87 68 PLA

FF88 68 PLA

FF89 A0 02 LDY #$02

FF8B 8C 8B FD STY $FD8B ; Set cartridge power off

FF8E C8 INY

FF8F 8C 8A FD STY $FD8A ; External power and cart address to output

FF92 8C F9 FF STY $FFF9 ; Memory map to 03 (Mikey and ROM addresses are RAM)

FF95 64 00 STZ $00 ; Set $0000 to 0

FF97 4C 19 FE JMP $FE19

; Public key for decryption

FF9A 35 B5 A3 94 28 06 D8 A2

FFA2 26 95 D7 71 B2 3C FD 56

FFAA 1C 4A 19 B6 A3 B0 26 00

FFB2 36 5A 30 6E 3C 4D 63 38

FFBA 1B D4 1C 13 64 89 36 4C

FFC2 F2 BA 2A 58 F4 FE E1 FD

FFCA AC 7E 79

; Mikey addresses to be initialized (add to Mikey range offset $FD00)

FFCD 90 .db $90 ; SDONEACK

FFCE 92 .db $92 ; DISPCTL

FFCF 95 .db $95 ; DISPADRH

FFD0 94 .db $94 ; DISPADRL

FFD1 93 .db $93 ; PBCKUP

FFD2 09 .db $09 ; TIM2CTLA

FFD3 08 .db $08 ; TIM2BCKUP

FFD4 BF .db $BF ; BLUEREDF

FFD5 AF .db $AF ; GREENF

FFD6 B0 .db $B0 ; BLUERED0

FFD7 A0 .db $A0 ; GREEN0

FFD8 01 .db $01 ; TIM0CTLA

FFD9 00 .db $00 ; TIM0BCKUP (also used as initialization value for SDONEACK)

; Initialization values for Mikey addresses (see also $FFCC)

FFD9 00 .db $00 ; Render sprite command (and also address TIM0BCKUP)

FFDA 0D .db $0D ; 4 bit color with video DMA enabled

FFDB 20 .db $20 ; Video address at $2000

FFDC 00 .db $00 ; Video address at $2000

FFDD 29 .db $29 ; Magic P value for screen frequency

FFDE 1F .db $1F ; Enable count and reload, linking

FFDF 68 .db $68 ; 104 backup value for vertical scan timer (== 102 vertical lines plus 2)

FFE0 3E .db $3E ; Yellow

FFE1 0E .db $0E ; Yellow

FFE2 00 .db $00 ; Black

FFE3 00 .db $00 ; Black

FFE4 18 .db $18 ; 2 microseconds timing for horizontal line

FFE5 9E .db $9E ; 158 backup value for horizontal line scan (160 pixel across)

; Suzy addresses to be initialized (add to Suzy range offset $FC00)

FFE6 91 .db $91 ; SPRGO

FFE7 11 .db $11 ; SCBNEXTH

FFE8 10 .db $10 ; SCBNEXTL

FFE8 09 .db $09 ; VIDBASH

FFEA 08 .db $08 ; VIDBASL

FFEB 06 .db $06 ; VOFFL

FFEC 04 .db $04 ; HOFFL

FFED 90 .db $90 ; SUZYBUSEN

FFEE 92 .db $92 ; SPRSYS

; Initialization values for Suzy addresses (see also $FFE6)

FFEF 01 .db $01 ; Draw sprite (no everon detection)

FFF0 50 .db $50 ; SCBNEXT = $5082

FFF1 82 .db $82 ;

FFF2 20 .db $20 ; VIDBAS = $2000

FFF3 00 .db $00 ;

FFF4 00 .db $00 ; VOFF = $0000

FFF5 00 .db $00 ; HOFF = $0000

FFF6 01 .db $01 ; Bus enabled

FFF6 00 .db $00 ; Unsigned math, no accumulation, collission on, normal handed

; Reserved registers

FFF8 00 .db $00 ;

FFF9 80 ; MEMCTL value

FFFA 00 30 ; NMI vector

FFFC 80 FF ; Boot vector

FFFE 80 FF ; IRQ vector

  • Like 2
Link to comment
Share on other sites

I guess we're already past the stage of open and clean Lynx boot ROMs, since Wookie has created a micro loader that is the bare minimum to load up a Lynx game (no splashscreen, e.g.). Check his work here:

Link to comment
Share on other sites

make a md5sum for each rom and depending on that read the dir at 410, 512 or 896. yes, no problem.

Why even bother with that? Just reimplement the algorithm as given above, giving compatibility with every existing ROM, every prototype yet to be rediscovered and every homebrew yet to be written.

Link to comment
Share on other sites

make a md5sum for each rom and depending on that read the dir at 410, 512 or 896. yes, no problem.

There is no guarantee of where the directory is or what it looks like.

 

The current cc65 bootloader is 52+171 = 223 bytes. And after that the dir starts. My dir happens to be 8 bytes per entry in the same format as usual. But Wookie had plans to skip the directory completely and use some kind of streaming data structures.

 

The correct way to find out what happens at boot time is to analyze the first byte.

 

If it is FF then the first entry is one block. If it is FE then it is two blocks etc.

 

Then you need to skip over as many blocks as there were.

 

The next thing is to repeat the process again. There will be some byte like FC for telling how many blocks we have at this time.

 

So the possibilities are:

 

FF + 51 bytes = 52 bytes (my bootloader at cc65.org)

FE + (2 * 51) + FA + (6 * 51) = 410 bytes (Harry Dodgson's bootloader)

 

Here is my bootloaders source.

 

	.segment "BOOTLDR"
;**********************************
; Here is the bootloader in plaintext
; The idea is to make the smallest possible encrypted loader as decryption
; is very slow. The minimum size is 49 bytes plus a zero byte.
;**********************************
;	   EXE = $f000
;
;	   .org $0200
;
;	   ; 1. force Mikey to be in memory
;	   stz	 MAPCTL
;
;	   ; 3. set ComLynx to open collector
;	   lda #4		  ; a = 00000100
;	   sta SERCTL	  ; set the ComLynx to open collector
;
;	   ; 4. make sure the ROM is powered on
;	   lda #8		  ; a = 00001000
;	   sta IODAT	   ; set the ROM power to on
;
;	   ; 5. read in secondary exe + 8 bytes (1st directory entry) from the cart and store it in $f000
;	   ldx #0		  ; x = 0
;	   ldy #$AB		; y = secondary loader size + 1st directory entry (171 bytes)
;rloop1: lda RCART0	 ; read a byte from the cart
;	   sta EXE,X	   ; EXE[X] = a
;	   inx			 ; x++
;	   dey			 ; y--
;	   bne rloop1	  ; loops until y wraps
;
;	   ; 6. jump to secondary loader
;	   jmp EXE		 ; run the secondary loader
;	  
;	  ; plus lots of zero bytes as there is empty space to fill the 49 bytes of code
;
;	   .reloc
;**********************************
; After compilation, encryption and obfuscation it turns into this.
;**********************************
	.byte $ff, $dc, $e3, $bd, $bc, $7f, $f8, $94
	.byte $b7, $dd, $68, $bb, $da, $5b, $50, $5c
	.byte $ea, $9f, $2b, $df, $96, $80, $3f, $7e
	.byte $ef, $15, $81, $ae, $ad, $e4, $6e, $b3
	.byte $46, $d7, $72, $58, $f7, $76, $8a, $4a
	.byte $c7, $99, $bd, $ff, $02, $3e, $5b, $3f
	.byte $0c, $49, $1b, $22

 

This bootloader does NOT need to know what kind of cart it resides on. It is so small that it can always load the secondary bootloader without a need to change blocks.

 

Then the secondary bootloader can be compiled to have the correct understanding of blocksizes. So this is different for different carts.

 

Cool :cool:

 

But as you see there is no reason why the bootloader needs a directory or a title sprite or anything else.

 

--

Karri

Edited by karri
  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Just to let you know.

 

I successfully did a lynxboot.img replacement based only on "free" sources, without reverse engenieering the original ROM (which is the major point to get it "free"). It might not work for all future ROMs but for what I tried until now it is working (used in mednafen). There are still two issues which I would like to solve before publishing esp I need to test more ROMs.

Edited by sage
Link to comment
Share on other sites

I successfully did a lynxboot.img replacement based only on "free" sources, without reverse engenieering the original ROM (which is the major point to get it "free").

 

Fantastic work! So it's a new drop-in ROM replacement for any old emulator, that I could in theory use on original hardware?

 

From a legal point of view, you're contaminated by having posted on this thread before producing your ROM, given that the original source code is listed right there at the top. Except that Hasbro's release of development rights into the public domain (which there are lots of second hand sources for, but I can't seem to find the original announcement) definitely means that nobody's ever actually going to sue.

 

It might not work for all future ROMs but for what I tried until now it is working (used in mednafen). There are still two issues which I would like to solve before publishing esp I need to test more ROMs.

 

I'm on-and-off working on a new development environment, in which you write whatever code you want for the encrypted bootstrap. If you wanted to go with the serial media metaphor then that'd be your own business, as I certainly won't be going to any effort to support it. I'm probably still a few weeks away from having any working ROMs but would it be helpful to send some over with a variety of boot schemes when the moment comes?

Link to comment
Share on other sites

I successfully did a lynxboot.img replacement based only on "free" sources, without reverse engenieering the original ROM (which is the major point to get it "free").

 

Fantastic work! So it's a new drop-in ROM replacement for any old emulator, that I could in theory use on original hardware?

if you replace your main cpu, yes.

 

From a legal point of view, you're contaminated by having posted on this thread before producing your ROM, given that the original source code is listed right there at the top. Except that Hasbro's release of development rights into the public domain (which there are lots of second hand sources for, but I can't seem to find the original announcement) definitely means that nobody's ever actually going to sue.

 

As you have actually no idea what and how I do it, I do not see how you can come to that conclusion.

 

It might not work for all future ROMs but for what I tried until now it is working (used in mednafen). There are still two issues which I would like to solve before publishing esp I need to test more ROMs.

 

I'm on-and-off working on a new development environment, in which you write whatever code you want for the encrypted bootstrap. If you wanted to go with the serial media metaphor then that'd be your own business, as I certainly won't be going to any effort to support it. I'm probably still a few weeks away from having any working ROMs but would it be helpful to send some over with a variety of boot schemes when the moment comes?

 

I do not understand your argument.

Link to comment
Share on other sites

As you have actually no idea what and how I do it, I do not see how you can come to that conclusion.

You have seen the original source code before reimplementing it. That's the legal test.

 

I do not understand your argument.

There is no argument there. It's a question.

Link to comment
Share on other sites

I'm on-and-off working on a new development environment, in which you write whatever code you want for the encrypted bootstrap. If you wanted to go with the serial media metaphor then that'd be your own business, as I certainly won't be going to any effort to support it.

 

I still dont understand neither your remark nor

I'm probably still a few weeks away from having any working ROMs but would it be helpful to send some over with a variety of boot schemes when the moment comes?

 

.. what you want to tell me with this sentence. I have no idea what your variety of "boot schemes" might be.

Link to comment
Share on other sites

I still dont understand neither your remark nor

See clean room design. Your design is not a clean room design. It is therefore not clean in the software sense. Hence its redistribution is of questionable legality — see Sony v Connectix, which I think is exactly applicable here as it covers a situation where the original ROM was disassembled in order to reproduce its functionality without copying its code. The original court decided for Sony, the first appeal court decided for Connectix and then Sony bought Connectix before it could get out of the US district courts. Which sadly leaves us without a pronouncement from a reliably senior court.

.. what you want to tell me with this sentence. I have no idea what your variety of "boot schemes" might be.

It means you write whatever code you want in the encrypted part of the cartridge (usually referred to as the boot loader, such as here or here) and then whatever code you want in the unencrypted part. Do whatever you want. Epyx's questionable decision to pretend there was a filing system is of no relevance. You could follow Harry Dodson's route and just load some data immediately after the encrypted portion and hop into it. You could use an Epyx-style filing system. You could use absolutely any other filing system, whether FAT16, ADFS, BetaDOS or whatever. You could encrypt your entire program code, if it's small.

 

EDIT: I think my tone has become too negative, so I'm editing to repeat that it's fantastic that you expend your time and energy into Mednafen and into other Lynx emulation improvement tasks such as this one. From what I can see, you do good work and are a benefit to the community, and I'm sure I'm not the only one that wants to say thanks.

Edited by ThomH
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...