On Geektimes in the summer there was an article about the Megaprocessor - a processor of discrete transistors and LEDs, which weighs half a ton and occupies the entire living room in an ordinary townhouse near Cambridge. I decided to take advantage of my geographical proximity to this megaproject, and program something presentable for it - for example, to sport my previous Digital Rain program for Megaprocessor.addq ±1 , addq ±2 ). But there are a couple of unexpected possibilities: a separate sqrt command, and the .wt mode for shift commands, which replaces the result with the sum of the extended bits. Thus you can, for example, a pair of commands ld.b r1, #15; lsr.wt r0, r1 ld.b r1, #15; lsr.wt r0, r1 calculate the number of single bits in r0 (a question so much favored by job interviewers!). The ln mnemonic for a command that loads an immediate value into the register (instead of the mnemonic mov usual for x86 or ARM) indicates the way it is executed: in fact, from the point of view of the processor, ld.b r1, (pc++) is executed.reset vector is used, and in the remaining vectors there is a “stub” from one reti instruction. (For x86 or ARM programmers, the return command from the interrupt handler is familiar under the iret mnemonic.) None of these interrupts in our program can happen anyway, so even the stubs could not be set for them. reset: jmp start; nop; ext_int: reti; nop; nop; nop; div_zero: reti; nop; nop; nop; illegal: reti; nop; nop; nop; seed for the current RNG value, and the position array of 32 values ​​— one for each “display column” —to keep track of where “drop” crawls in this column. We initialize the array with just 32 random bytes. The jsr command — subroutine call — matches the call in x86 or bl in ARM. start: ld.w r0, #0x2000; move sp, r0; // set random positions ld.b r1, #32; init_loop: jsr rand; // returns random value in r0 ld.b r2, #position; add r2, r1; st.b (r2), r0; addq r1, #-1; bne init_loop; (#position + r1) one command, you first have to calculate the address (#position + r1) separate addition command. busy_loop: ld.b r1, #32; next_col: ld.b r2, #position; add r2, r1; ld.b r0, (r2); addq r0, #2; addq r0, #2; btst r0, #8; beq save; jsr rand; save: st.b (r2), r0; addq r1, #-1; bmi busy_loop; addq r0, #2 4 with one command, so we repeat addq r0, #2 twice, and then check the eighth bit of the result to determine if it has exceeded the value of 255. If it has exceeded, then save the new random value to the position array; otherwise, save the old one, incremented by 4. The conditional branch command bmi moves to the beginning of the busy_loop cycle if the result of the last action is negative, i.e. after processing the zero column. rand: ld.w r0, seed; ld.w r1, #33797; mulu; addq r2, #1; st.w seed, r2; move r0, r2; ret; rand , we have the index of the “display column” in r1 , and it needs to be saved and restored; and then in r2 should be an offset (#position + r1) . So you can put this offset into rand calculation: rand: push r1; // ! ld.w r0, seed; ld.w r1, #33797; mulu; addq r2, #1; st.w seed, r2; pop r1; // ! move r0, r2; ld.b r2, #position; // ! add r2, r1; // ! ret; start: ld.w r0, #0x2000; move sp, r0; // set random positions ld.b r1, #32; init_loop: jsr rand; st.b (r2), r0; addq r1, #-1; bne init_loop; busy_loop: ld.b r1, #32; next_col: ld.b r2, #position; add r2, r1; ld.b r0, (r2); addq r0, #2; addq r0, #2; btst r0, #8; beq save; jsr rand; save: st.b (r2), r0; addq r1, #-1; bmi busy_loop; ld.b r2, #position; add r2, r1; ld.b r2, #position; add r2, r1; at the beginning of the next_col cycle, next_col can replace it by jumping into the rand subroutine: rand: push r1; ld.w r0, seed; ld.w r1, #33797; mulu; addq r2, #1; st.w seed, r2; pop r1; move r0, r2; add_position: ld.b r2, #position; add r2, r1; ret; start: <...> busy_loop: ld.b r1, #32; next_col: jsr add_position; // ! ld.b r0, (r2); addq r0, #2; addq r0, #2; btst r0, #8; beq save; jsr rand; save: st.b (r2), r0; addq r1, #-1; bmi busy_loop; next_col cycle, which will draw the “drop” on the display. move r3, r1; // x (0..1f) lsr r3, #3; // byte addr in row (0..3) ld.b r2, #0xfc; // y mask and r2, r0; // y * 4 (0..fc) add r3, r2; // byte addr in screen ld.w r2, #0xa000; add r3, r2; // byte addr in memory ld.b r2, #2; lsr.wt r0, r2; ld.b r2, #7; and r2, r1; // bit index in byte (0..7) lsl r2, #1; lsr r0, #2; roxr r2, #1; ld.b r0, (r3); // and now apply test r2; bpl blank; bset r0, r2; jmp apply; blank: bclr r0, r2; apply: st.b (r3), r0; jmp next_col; r1 , and the position and “color” of a drop is in r0 , the byte address is calculated as (r1 >> 3) + (r0 & 0xfc) + 0xa000 . After that, the commands ld.b r2, #2; lsr.wt r0, r2; ld.b r2, #2; lsr.wt r0, r2; we determine the color of the drop: if both lower bits in r0 have been set, then as a result of these commands in r0 will be a value of 2; otherwise, the value is 0 or 1. Finally, in the three lower bits of r2 we memorize the number of the required bit of the “video memory”, and “push” into the high bit of r2 color of the drop with the sequence lsl r2, #1; lsr r0, #2; roxr r2, #1; lsl r2, #1; lsr r0, #2; roxr r2, #1; - the second command pushes the color bit from r0 to the CF flag, and the last (cyclic right shift with CF) pushes this bit into r2 . When the registers are not enough for all the necessary values, you have to be clever! Finally, a byte is retrieved from the “video memory” at the desired address, and depending on the color bit, this byte is either set or the required bit is reset. The bset and bclr use only the lower bits of their second operand, so the color bit in the high bit r2 does not interfere with them. We check this high bit with the sequence test r2; bpl blank; test r2; bpl blank; - the conditional jump command bpl performs the jump if the result of the last action is positive, i.e. bit color shot. reset: jmp start; nop; ext_int: reti; nop; nop; nop; div_zero: reti; nop; nop; nop; illegal: reti; nop; nop; nop; rand: push r1; ld.w r0, seed; ld.w r1, #33797; mulu; addq r2, #1; st.w seed, r2; pop r1; move r0, r2; add_position: ld.b r2, #position; add r2, r1; ret; start: ld.w r0, #0x2000; move sp, r0; // set random positions ld.b r1, #32; init_loop: jsr rand; st.b (r2), r0; addq r1, #-1; bne init_loop; busy_loop: ld.b r1, #32; next_col: jsr add_position; ld.b r0, (r2); addq r0, #2; addq r0, #2; btst r0, #8; beq save; jsr rand; save: st.b (r2), r0; addq r1, #-1; bmi busy_loop; move r3, r1; // x (0..1f) lsr r3, #3; // byte addr in row (0..3) ld.b r2, #0xfc; // y mask and r2, r0; // y * 4 (0..fc) add r3, r2; // byte addr in screen ld.w r2, #0xa000; add r3, r2; // byte addr in memory ld.b r2, #2; lsr.wt r0, r2; ld.b r2, #7; and r2, r1; // bit index in byte (0..7) lsl r2, #1; lsr r0, #2; roxr r2, #1; ld.b r0, (r3); // and now apply test r2; bpl blank; bset r0, r2; jmp apply; blank: bclr r0, r2; apply: st.b (r3), r0; jmp next_col; seed: dw 1; position:; busy_loop will first light and then extinguish each drop. On the lighting semi-iteration, it will be necessary to set two bits of video memory: for the current position of the “drop” and for the previous one (canceled by the last semi-iteration).ld.b r2, #2; lsr.wt r0, r2; ) with the fixed value #2 with the flag variable, which will have the value 2 on the igniting half-iteration, and 1 on extinguishing: busy_loop: ld.b r1, #3; // ! ld.b r2, flag; // ! sub r1, r2; // ! st.b flag, r1; // ! ld.b r1, #32; next_col: jsr add_position; ld.b r0, (r2); ld.b r3, flag; // ! lsr r3, #1; // ! lsl r3, #2; // ! add r0, r3; // ! btst r0, #8; beq save; jsr rand; save: st.b (r2), r0; addq r1, #-1; bmi busy_loop; move r3, r1; // x (0..1f) lsr r3, #3; // byte addr in row (0..3) ld.b r2, #0xfc; // y mask and r2, r0; // y * 4 (0..fc) add r3, r2; // byte addr in screen ld.w r2, #0xa000; add r3, r2; // byte addr in memory ld.b r2, flag; // ! lsr.wt r0, r2; busy_loop loop busy_loop we subtract the current flag value from 3, i.e. change 2 by 1, and 1 by 2. Instead of moving the “drop” down at each iteration ( addq r0, #2; addq r0, #2; ), we add to r0 value (flag >> 1) << 2 , t . 4 on igniting semi-iteration, and 0 on quenching. // and now apply test r2; bpl blank; bset r0, r2; st.b (r3), r0; // ! addq r3, #-2; // ! addq r3, #-2; // ! btst r3, #8; // ! bne next_col; // ! ld.b r0, (r3); // ! bset r0, r2; // ! jmp apply; blank: bclr r0, r2; apply: st.b (r3), r0; jmp next_col; btst r3, #8; bne next_col; btst r3, #8; bne next_col; ensures that we do not go beyond the top edge of the "display" and do not try to write something at 0x9FFx. reset: jmp start; nop; ext_int: reti; nop; nop; nop; div_zero: reti; nop; nop; nop; illegal: reti; nop; nop; nop; rand: push r1; ld.w r0, seed; ld.w r1, #33797; mulu; addq r2, #1; st.w seed, r2; pop r1; move r0, r2; add_position: ld.b r2, #position; add r2, r1; ret; start: ld.w r0, #0x2000; move sp, r0; // set random positions ld.b r1, #32; init_loop: jsr rand; st.b (r2), r0; addq r1, #-1; bne init_loop; busy_loop: ld.b r1, #3; ld.b r2, flag; sub r1, r2; st.b flag, r1; ld.b r1, #32; next_col: jsr add_position; ld.b r0, (r2); ld.b r3, flag; lsr r3, #1; lsl r3, #2; add r0, r3; btst r0, #8; beq save; jsr rand; save: st.b (r2), r0; addq r1, #-1; bmi busy_loop; move r3, r1; // x (0..1f) lsr r3, #3; // byte addr in row (0..3) ld.b r2, #0xfc; // y mask and r2, r0; // y * 4 (0..fc) add r3, r2; // byte addr in screen ld.w r2, #0xa000; add r3, r2; // byte addr in memory ld.b r2, flag; lsr.wt r0, r2; ld.b r2, #7; and r2, r1; // bit index in byte (0..7) lsl r2, #1; lsr r0, #2; roxr r2, #1; ld.b r0, (r3); // and now apply test r2; bpl blank; bset r0, r2; st.b (r3), r0; addq r3, #-2; addq r3, #-2; btst r3, #8; bne next_col; ld.b r0, (r3); bset r0, r2; jmp apply; blank: bclr r0, r2; apply: st.b (r3), r0; jmp next_col; seed: dw 1; flag: db 2; position:; Source: https://habr.com/ru/post/309654/
All Articles