.include "4434def.inc" ; DZ: the timings here assume a 4.0 MHZ clock ;------------------------------------------------------------- ; register variable "declarations" ;------------------------------------------------------------- .def gbregnum=r16 .def gbregval=r17 .def temp=r18 .def xvar=r19 .def yvar=r20 .def zvar=r21 .def ichar=r22 .def ochar=r22 .def addrhi=r23 .def addrlo=r24 .def memdata=r25 .def addata=r25 ;------------------------------------------------------------- ; table for reset and interrupt handler and so forth ;------------------------------------------------------------- .cseg .org $0000 rjmp Reset ;------------------------------------------------------------- ; main program loop ;------------------------------------------------------------- Main: rcall InitComm rcall InitMem rcall FillMem NextCommand: ;rcall Blinky ; blink the LED to show we are ready ldi ichar, 0 rcall CharIn ; fetch the command code cpi ichar, 1 ; is this a read? breq ReadReq cpi ichar, 2 ; is this a write? breq WriteReq cpi ichar, 65 ; imagetest test breq CmdTest cpi ichar, 66 ; take a snapshot breq CmdSnapshot cpi ichar, 67 ; transfer the image breq CmdTransfer rjmp NextCommand ; neither a read nor a write CmdTest: sbi CCLK_CTL_REG, CCLK_BIT ; enable the clock rcall GetADVal mov ochar, addata rcall CharOut rjmp NextCommand CmdSnapshot: ; take a snapshot and store it in the SRAM rcall GB_GetPicture ldi ochar, 1 rcall CharOut rjmp NextCommand CmdTransfer: ldi yvar, 0 ctloop1: ldi xvar, 0 ctloop2: mov addrhi, yvar mov addrlo, xvar rcall ReadMem mov ochar, memdata rcall CharOut inc xvar brne ctloop2 inc yvar cpi yvar, 4 ; 256 * 4 bytes total to read brne ctloop1 rjmp NextCommand ReadReq: rcall CharIn ; read high byte of memory address mov addrhi, ichar rcall CharIn ; read low byte of memory address mov addrlo, ichar rcall ReadMem ; fetch the data at that location mov ochar, memdata rcall CharOut ; send the result back rjmp NextCommand WriteReq: rcall CharIn ; read high byte of memory address mov addrhi, ichar rcall CharIn ; read low byte of memory address mov addrlo, ichar rcall CharIn ; read the data to be stored mov memdata, ichar rcall WriteMem ; write the data to SRAM mov ochar, memdata rcall CharOut ; echo the data byte back to caller rjmp NextCommand ;-------------------------------------------------------------- ; Gameboy camera functions ;-------------------------------------------------------------- ; ; Theory of operation: ; Send a reset ; Set register 0 (Z + O) to 0x9F (positive signal, 32mv offset [?]) ; Set register 1 (N, VH, G) to 0x07 ; Set register 2 (C1) to 0x08 and register 3 (C0) to 0xFF ; Set register 4 (P) to 0x01 ; Set register 5 (M) to 0x00 ; Set register 6 (X) to 0x01 ; Set register 7 (E, I, V) to 0x03 ; ; Then, send a "start" command ; and get the data .equ R0_NO_CALIBRATION =$00 .equ R0_POSITIVE_CALIBRATION =$80 .equ R0_NEGATIVE_CALIBRATION =$40 .equ R0_VOLTAGE_OFFSET_NEGATIVE =$20 .equ R0_VOLTAGE_OFFSET_POSITIVE =$00 ; the rest of register 0 (low 5 bits) are the voltage offset. try using 1 .equ R1_WEIRD_VERTICAL_ENHANCE_MODE =$80 .equ R1_NO_EDGEOP =$00 .equ R1_HORZ_EDGEOP =$20 .equ R1_VERT_EDGEOP =$40 .equ R1_2D_EDGEOP =$60 ; the rest of register 1 (low 5 bits) determine output gain. try using 0x07 ; register 2 is the high part of the exposure setting ; register 3 is the low part of the exposure setting ; the 16-bit value formed by these is multiplied by 16 microseconds to get total ; example: R2 = 0x08 R3 = 0xFF gives about 36 milliseconds. ; DZ: trying different values for these could be a decent calibration idea... .equ R4_NORMAL_P =$01 .equ R4_EDGE_P =$02 .equ R5_NORMAL_M =$00 .equ R5_VERTICAL_ENHANCE_M =$02 .equ R5_ENHANCE_M =$05 .equ R6_NORMAL_X =$01 .equ R7_EDGE_ENHANCE_MODE =$00 .equ R7_EDGE_EXTRACT_MODE =$80 .equ R7_EDGE_50 =$00 .equ R7_EDGE_75 =$10 .equ R7_EDGE_100 =$20 .equ R7_EDGE_125 =$30 .equ R7_EDGE_200 =$40 .equ R7_EDGE_300 =$50 .equ R7_EDGE_400 =$60 .equ R7_EDGE_500 =$70 .equ R7_I_INVERTED =$08 ; the rest of register 7 (low 3 bits) sets reference voltage. try 0x03 .equ CCLK_CTL_REG =DDRC .equ CCLK_REG =PORTC .equ CCLK_BIT =6 .equ CSTART_CTL_REG =DDRC .equ CSTART_REG =PORTC .equ CSTART_BIT =7 .equ CREAD_CTL_REG =DDRD .equ CREAD_REG =PIND .equ CREAD_BIT =2 .equ CRESET_CTL_REG =DDRD .equ CRESET_REG =PORTD .equ CRESET_BIT =3 .equ CLOAD_CTL_REG =DDRD .equ CLOAD_REG =PORTD .equ CLOAD_BIT =4 .equ CSIN_CTL_REG =DDRD .equ CSIN_REG =PORTD .equ CSIN_BIT =5 ; this function sets the system up for "camera mode" which ; is needed when communicating with the camera. ; total time at 4 mhz: just under 4 microseconds GB_SetCameraMode: cli sbi CCLK_CTL_REG, CCLK_BIT sbi CSTART_CTL_REG, CSTART_BIT cbi CREAD_CTL_REG, CREAD_BIT ; cread is an input sbi CRESET_CTL_REG, CRESET_BIT sbi CRESET_REG, CRESET_BIT ; set reset hi sbi CLOAD_CTL_REG, CLOAD_BIT sbi CSIN_CTL_REG, CSIN_BIT sei ret ; reset the camera GB_Reset: ; DZ: this doesn't match the timing sheets on page 2... cli cbi CRESET_REG, CRESET_BIT ; reset lo sbi CCLK_REG, CCLK_BIT ; clock hi sbi CRESET_REG, CRESET_BIT ; reset hi cbi CCLK_REG, CCLK_BIT ; clock lo sei ret GB_Start: cli cbi CCLK_REG, CCLK_BIT ; clock lo sbi CSTART_REG, CSTART_BIT ; start hi sbi CCLK_REG, CCLK_BIT ; clock hi cbi CSTART_REG, CSTART_BIT ; start lo cbi CCLK_REG, CCLK_BIT ; clock lo sei ret ; set a register in the GB Camera ; note: this destroys the values in gbregnum and gbregval GB_SetRegister: cli ldi temp, 3 ; initialize bit counter for regnum cbi CLOAD_REG, CLOAD_BIT ; cload lo GB_SR0: cbi CCLK_REG, CCLK_BIT ; clock lo cbi CSIN_REG, CSIN_BIT ; csin lo sbrc gbregnum, 2 ; skip instr if bit 2 of gbregnum clear sbi CSIN_REG, CSIN_BIT ; if bit 2 was set, set csin sbi CCLK_REG, CCLK_BIT ; clock hi lsl gbregnum ; get the next bit dec temp brne GB_SR0 ; if there are more bits to do, do them ldi temp, 8 ; initialize bit counter for regval GB_SR1: cbi CCLK_REG, CCLK_BIT ; clock lo cbi CSIN_REG, CSIN_BIT ; csin lo sbrc gbregval, 7 ; skip instr if bit 7 of gbregval clear sbi CSIN_REG, CSIN_BIT ; if bit 7 was set, set csin sbi CCLK_REG, CCLK_BIT ; clock hi lsl gbregval ; get the next bit dec temp brne GB_SR1 ; if there are more bits to do, do them sbi CLOAD_REG, CLOAD_BIT ; set cload hi cbi CCLK_REG, CCLK_BIT ; clock lo cbi CLOAD_REG, CLOAD_BIT ; set cload lo sei ret GB_GetPicture: rcall FillMem rcall GB_SetCameraMode ; allow the outputs for camera mode rcall GB_Reset ; reset the camera ldi gbregnum, 0; ; camera register 0 ldi gbregval, (R0_POSITIVE_CALIBRATION | 0x1F) rcall GB_SetRegister ldi gbregnum, 1 ; camera register 1 ldi gbregval, (R1_NO_EDGEOP | 0x07) rcall GB_SetRegister ldi gbregnum, 2 ; camera register 2 ldi gbregval, 0x08 rcall GB_SetRegister ldi gbregnum, 3 ; camera register 3 ldi gbregval, 0xFF rcall GB_SetRegister ldi gbregnum, 4 ; camera register 4 ldi gbregval, R4_NORMAL_P rcall GB_SetRegister ldi gbregnum, 5 ; camera register 5 ldi gbregval, R5_NORMAL_M rcall GB_SetRegister ldi gbregnum, 6 ; camera register 6 ldi gbregval, R6_NORMAL_X rcall GB_SetRegister ldi gbregnum, 7 ; camera register 7 ldi gbregval, 0x03 rcall GB_SetRegister rcall GB_Start WaitForRead: cbi CCLK_REG, CCLK_BIT nop sbi CCLK_REG, CCLK_BIT in temp, CREAD_REG sbrs temp, CREAD_BIT rjmp WaitForRead cbi CCLK_REG, CCLK_BIT ldi temp, 0x03 ; two low bits of port B are used as outputs out DDRB, temp ; ok, start has been set, so now clock in the data ldi addrhi, 0 ldi addrlo, 0 ; dummy pulse to get the pixel stuff started sbi CCLK_REG, CCLK_BIT ; clock hi nop nop cbi CCLK_REG, CCLK_BIT ; clock lo nop nop ; this chunk of code reads an actual line of data GetRow: ldi xvar, 32 ; get a pixel GetNext4: sbi CCLK_REG, CCLK_BIT ; clock hi in memdata, PINB lsr memdata cbi CCLK_REG, CCLK_BIT ; clock lo lsr memdata sbi CCLK_REG, CCLK_BIT ; clock hi (dummy (skip)) out PORTA, addrlo ; send low part of address out PORTB, addrhi ; send high part of address cbi CCLK_REG, CCLK_BIT ; clock lo sbi PORTD, 7 ; OE high sbi CCLK_REG, CCLK_BIT ; clock hi (dummy (skip)) ldi temp, 0x3F ; set the data bits in port C for output cbi CCLK_REG, CCLK_BIT ; clock lo out DDRC, temp out PORTC, memdata ; output the data (make sure clock is low!) sbi CCLK_CTL_REG, CCLK_BIT cbi PORTD, 6 ; WE low sbi CCLK_REG, CCLK_BIT ; clock hi (dummy (skip)) sbi PORTD, 6 ; WE high cbi PORTD, 7 ; OE low cbi CCLK_REG, CCLK_BIT ; clock lo inc addrlo brne FourDone inc addrhi cpi addrhi, 4 breq PictureDone FourDone: dec xvar brne GetNext4 ; ok, a 128-byte row has been read from the chip. now, we need ; to read three more rows but not do anything with them. ldi yvar, 3 ldi xvar, 128 GetDummy: sbi CCLK_REG, CCLK_BIT ; clock hi nop nop nop cbi CCLK_REG, CCLK_BIT ; clock lo dec xvar brne GetDummy dec yvar breq GetRow ldi xvar, 128 rjmp GetDummy PictureDone: ret ;-------------------------------------------------------------- ; RS232 communication functions ;-------------------------------------------------------------- InitComm: sbi UCR, TXEN sbi UCR, RXEN ldi temp, 25 out UBRR, temp ret CharIn: sbis USR, RXC rjmp CharIn in ichar, UDR ret CharOut: sbis USR, UDRE rjmp CharOut out UDR, ochar ret ;-------------------------------------------------------------- ; SRAM Memory functions ;-------------------------------------------------------------- InitMem: ldi temp, 0xFF ; set port A to be output (address) out DDRA, temp sbi DDRD, 6 ; bit 6 is the RD/WR memory control sbi DDRD, 7 ; bit 7 is OE ret WriteMem: ldi temp, 0x03 ; two low bits of port B are used as outputs here out DDRB, temp andi addrhi, 0x03 ; make sure address is only ten bits out PORTA, addrlo ; send low part of address out PORTB, addrhi ; send high part of address sbi PORTD, 7 ; OE high ldi temp, 0x3F ; set the data bits in port C for output out DDRC, temp out PORTC, memdata ; output the data cbi PORTD, 6 ; WE low sbi PORTD, 6 ; WE high cbi PORTD, 7 ; OE low ret ReadMem: ldi temp, 0x00 ; set the data bits in port C for input out DDRC, temp ldi temp, 0x03 ; two low bits of port B are used as outputs here out DDRB, temp andi addrhi, 0x03 ; make sure address is only ten bits out PORTA, addrlo ; send low part of address out PORTB, addrhi ; send high part of address sbi PORTD, 6 ; WE high cbi PORTD, 7 ; OE low in memdata, PINC ; load the memory andi memdata, 0x3F ; not using the upper two bits ret ;-------------------------------------------------------- ; reset handler; stack pointer setup ;-------------------------------------------------------- Reset: ldi temp, low(RAMEND) ; set the high and low parts of the SP to out SPL, temp ; the end of memory ldi temp, high(RAMEND) out SPH, temp rjmp Main ;-------------------------------------------------------------- ; A/D functions ;-------------------------------------------------------------- GetADVal: cli ldi temp, 0x03 ; set the upper six bits for input out DDRB, temp sbi CCLK_REG, CCLK_BIT ; clock hi nop cbi CCLK_REG, CCLK_BIT ; clock lo nop sbi CCLK_REG, CCLK_BIT ; clock hi nop cbi CCLK_REG, CCLK_BIT ; clock lo nop sbi CCLK_REG, CCLK_BIT ; clock hi in temp, PINB cbi CCLK_REG, CCLK_BIT ; clock lo andi temp, 0xFC lsr temp lsr temp mov addata, temp sei ret ;-------------------------------------------------------------- ; debugging functions ;-------------------------------------------------------------- ; delay procedure. busy-waits for a while Delay: ldi zvar, 244 loop1: ldi yvar, 0 loop2: ldi xvar, 0 loop3: inc xvar brne loop3 inc yvar brne loop2 inc zvar brne loop1 ret ; blink the LED. mainly used for debugging Blinky: ldi temp, 0x80 ; while blinking, only B7 is an output out DDRB, temp ldi temp, 0x80 out PORTB, temp ; on rcall Delay ldi temp, 0x00 out PORTB, temp ; off rcall Delay ret ; FillMem: debugging function... fill 1k of SRAM memory with a pattern FillMem2: ldi xvar, 0 fmloop: ldi memdata, 0xFF eor memdata, xvar ; xor of address goes into memory mov addrlo, xvar rcall WriteMem inc xvar brne fmloop ret FillMem: ldi addrhi, 0 rcall FillMem2 inc addrhi rcall FillMem2 inc addrhi rcall FillMem2 inc addrhi rcall FillMem2 ret