├── .gitignore ├── program1.txt ├── PIC_10F200-xxx-to-2006-xxx_e.pdf ├── program2.txt ├── README.txt ├── pic_register.erl ├── pic_compiler.pl ├── new_pic_emulator.erl └── pic_emulator.erl /.gitignore: -------------------------------------------------------------------------------- 1 | *.beam 2 | *.picc 3 | *.swp 4 | RCS 5 | -------------------------------------------------------------------------------- /program1.txt: -------------------------------------------------------------------------------- 1 | MOVLW 10 2 | MOVWF 16 3 | MOVLW 5 4 | ADDWF 16, 0 5 | -------------------------------------------------------------------------------- /PIC_10F200-xxx-to-2006-xxx_e.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viklund/pic-emulator/master/PIC_10F200-xxx-to-2006-xxx_e.pdf -------------------------------------------------------------------------------- /program2.txt: -------------------------------------------------------------------------------- 1 | MOVLW 16 2 | MOVWF 4 3 | MOVLW 15 4 | MOVWF 0 5 | INCF 4,1 6 | BTFSC 4,4 7 | GOTO 2 8 | MOVLW 0 9 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Currently the plan is to emulate the PIC200. 2 | 3 | The main aim of this project is to learn the Erlang programming language. 4 | 5 | I also want to build something with PICs (or any other microcontroller), but I 6 | don't know how they work so I thought that an emulator would be a good way to 7 | learn how they function. 8 | -------------------------------------------------------------------------------- /pic_register.erl: -------------------------------------------------------------------------------- 1 | -module(pic_register). 2 | -export([ new/0, new/2, get/2, set/3, print/1 ]). 3 | 4 | % register, 32 bytes. Divided into SFR (Special Function Registers) and GPR 5 | % 0-7 SFR and 16 GPR 6 | % 0 -> INDF, Uses content of FSR to address data memory 7 | % 1 -> TMR0, 8-bit real-time clock/counter 8 | % 2 -> PCL, Low-order 8 bits of PC (Program counter) 9 | % 3 -> STATUS, Status of ALU 10 | % 4 -> FSR, Indirect data memory access pointer 11 | % 5 -> OSCCAL 12 | % 6 -> GPIO last 4 bits only. 13 | % 7 -> CMCON0 (pic204 only). 14 | % 16-31 -> GPR 15 | 16 | new() -> new(32,8). 17 | new(Length,Width) -> lists:duplicate(Length,<<0:Width>>). 18 | 19 | get(N, L) -> lists:nth(N+1,L). 20 | 21 | set(0, V, L) -> set_fsr(V, L); 22 | set(1, V, L) -> simple_set(1, V, L); % Timer 23 | set(2, V, L) -> simple_set(2, V, L); % Program counter 24 | set(3, V, L) -> mask_set(3, V, L, 2#11000000); % Status register 25 | set(4, V, L) -> mask_set(4, V, L,2#00011111); % fsr addr 26 | set(5, V, L) -> simple_set(5, V, L); % osccal 27 | set(6, V, L) -> mask_set(6, V, L, 2#00000111); % GPIO 28 | 29 | set(N, _, L) when N < 16 -> L; % NO-OP 30 | set(N, V, L) when N < 32 -> simple_set(N, V, L). 31 | 32 | set_fsr(V,L) -> 33 | <> = get(4, L), 34 | case Addr of 35 | 0 -> L; 36 | _ -> set(Addr, V, L) 37 | end. 38 | 39 | mask_set(N,<>,L,Mask) -> 40 | %<> = get(N,L), 41 | NV = V band Mask, % bor OldValue, 42 | simple_set(N, <>, L). 43 | 44 | simple_set(N, V, L) -> 45 | {H,[_|T]} = lists:split(N,L), 46 | H ++ [V] ++ T. 47 | 48 | print(R) -> 49 | print(R,0). 50 | 51 | print([],_) -> io:nl(); 52 | print([_|R],A) when A > 7, A < 16 -> print(R,A+1); 53 | print([<>|R],A) -> 54 | case A of 55 | 0 -> io:format(" SPR:~n "); 56 | 16 -> io:format("GPR:~n "); 57 | _ -> nil 58 | end, 59 | case A rem 4 of 60 | 3 -> io:format(" ~2.10B : ~8.2.0B ~3.10B~n ",[A,H,H]), print(R,A+1); 61 | _ -> io:format(" ~2.10B : ~8.2.0B ~3.10B", [A,H,H]), print(R,A+1) 62 | end. 63 | -------------------------------------------------------------------------------- /pic_compiler.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use Modern::Perl; 3 | use autodie; 4 | use String::Util qw/ crunch /; 5 | 6 | my $text_file = shift; 7 | 8 | if (! $text_file || ! -f $text_file ) { 9 | say "Nothing to compile"; 10 | exit 1; 11 | } 12 | 13 | open my $P, '<', $text_file; 14 | my @program = map crunch($_),<$P>; 15 | close $P; 16 | 17 | my $opcode_table = opcode_table(); 18 | 19 | my @opcodes = map { token2opcode($_, $opcode_table) } @program; 20 | say STDERR "OPCODES: ", join(', ', @opcodes); 21 | my $binary = twelve_bit_pack(@opcodes); 22 | 23 | (my $out_file = $text_file) =~ s/\..*?$//; 24 | $out_file .= '.picc'; 25 | open my $O, '>', $out_file; 26 | binmode $O; 27 | print $O $binary; 28 | close $O; 29 | 30 | exit(0); 31 | 32 | sub token2opcode { 33 | my $t = shift; # format is "OPCODE ARG1,? ARG2?" 34 | my $opcode_table = shift; 35 | my ($op, @args) = extract_token($t); 36 | my $sub = $opcode_table->{$op}; 37 | return $sub->(@args); 38 | } 39 | 40 | sub extract_token { 41 | my $t = shift; 42 | 43 | my ($op, $args) = $t =~ /^ \s* (\S+) \s+ (\S.*?) \s* $/x; 44 | $args =~ s/\s+//g; 45 | my @args = split /,/, $args; 46 | return ($op, @args); 47 | } 48 | 49 | # Takes list of 12-bit values, pairs them up into 24 bit values which are then 50 | # splitted into 3 8-bit values and then packed as unsigned chars. 51 | sub twelve_bit_pack { 52 | my @bits = @_; # List of 12-bit values 53 | if ( @bits % 2 != 0 ) { 54 | push @bits, 0; # A no-op bit at the end 55 | } 56 | my $r; 57 | my @bits8; 58 | while ( my @vals = splice(@bits,0,2) ) { 59 | my $new_val = ($vals[0] << 12) | $vals[1]; 60 | push @bits8, ($new_val & (0b1111_1111 << 16)) >> 16; # Left-most 8-bits 61 | push @bits8, ($new_val & (0b1111_1111 << 8)) >> 8; # Middle 8-bits 62 | push @bits8, $new_val & (0b1111_1111 << 0); # Right-most 8-bits 63 | } 64 | pack('C*', @bits8); 65 | } 66 | 67 | sub opcode_table { 68 | my %opcode_table = ( 69 | NOP => '0000_0000_0000', 70 | OPTION => '0000_0000_0010', 71 | SLEEP => '0000_0000_0011', 72 | CLRWDT => '0000_0000_0100', 73 | TRIS => '0000_0000_01ff', 74 | MOVWF => '0000_001_fffff', 75 | CLRW => '0000_010_00000', # '0000_010_xxxxx', 76 | CLRF => '0000_011_fffff', 77 | SUBWF => '0000_10d_fffff', 78 | DECF => '0000_11d_fffff', 79 | IORWF => '0001_00d_fffff', 80 | ANDWF => '0001_01d_fffff', 81 | XORWF => '0001_10d_fffff', 82 | ADDWF => '0001_11d_fffff', 83 | MOVF => '0010_00d_fffff', 84 | COMF => '0010_01d_fffff', 85 | INCF => '0010_10d_fffff', 86 | DECFSZ => '0010_11d_fffff', 87 | RRF => '0011_00d_fffff', 88 | RLF => '0011_01d_fffff', 89 | SWAPF => '0011_10d_fffff', 90 | INCFSZ => '0011_11d_fffff', 91 | BCF => '0100_bbb_fffff', 92 | BSF => '0101_bbb_fffff', 93 | BTFSC => '0110_bbb_fffff', 94 | BTFSS => '0111_bbb_fffff', 95 | RETLW => '1000_kkkk_kkkk', 96 | CALL => '1001_kkkk_kkkk', 97 | GOTO => '101_kkkkk_kkkk', 98 | MOVLW => '1100_kkkk_kkkk', 99 | IORLW => '1101_kkkk_kkkk', 100 | ANDLW => '1110_kkkk_kkkk', 101 | XORLW => '1111_kkkk_kkkk', 102 | ); 103 | 104 | for my $key (keys %opcode_table) { 105 | $opcode_table{$key} = pattern2sub( $opcode_table{$key} ); 106 | } 107 | 108 | return \%opcode_table; 109 | } 110 | 111 | sub pattern2sub { 112 | my $pattern = shift; 113 | $pattern =~ s/_//g; 114 | 115 | if ( $pattern =~ /^([01]+)(b+|d)?(f+|k+)?/ ) { 116 | my ($op, $second_arg, $first_arg) = ($1,$2,$3); 117 | $second_arg ||= ''; 118 | 119 | my $f_width = length($first_arg); 120 | my $s_width = length($second_arg); 121 | 122 | if (!$first_arg && !$second_arg) { 123 | return sub { oct("0b$op") }; 124 | } 125 | 126 | return sub { 127 | my ($a1,$a2) = @_; 128 | if ( $a1 > 1 << $f_width ) { die "Arg1 too big" } 129 | if ( $second_arg && !defined($a2) ) { die "Need two args" } 130 | if ( $a2 && $a2 > 1 << $s_width ) { die "Arg2 too big" } 131 | 132 | my $format_str = "0b$op" 133 | . ( $s_width ? "%0$s_width".'b' : '' ) 134 | . "%0$f_width".'b'; 135 | return oct(sprintf($format_str, ($s_width ? ($a2,$a1) : $a1) )); 136 | }; 137 | } 138 | else { 139 | say "Can't parse $pattern"; 140 | } 141 | } 142 | 143 | -------------------------------------------------------------------------------- /new_pic_emulator.erl: -------------------------------------------------------------------------------- 1 | -module(new_pic_emulator). 2 | -compile(export_all). 3 | 4 | -record(pic, { 5 | type, 6 | 7 | accumulator, 8 | register_file, 9 | callstack, 10 | program, 11 | 12 | special_registers, % orddict 13 | opcodes, % fun/1 14 | 15 | instruction_register, % Bin value of next instr 16 | current_opcode, % Some kind of opcode desc, possibly an orddict 17 | current_value % Either value of last execution or last fetch 18 | } 19 | ). 20 | 21 | start_clock(P) -> 22 | Pid = spawn( fun() -> clock({q1,q2,q3,q4},1,P) end ), 23 | register(clock, Pid), 24 | Pid. 25 | 26 | stop_clock() -> 27 | clock ! exit, 28 | unregister(clock). 29 | 30 | clock(T,C,Target) -> 31 | receive 32 | exit -> ok 33 | after 100 -> 34 | Target ! element(C,T), 35 | clock(T, C rem 4 + 1, Target) 36 | end. 37 | 38 | start_pic(Pic) -> 39 | Pid = spawn( fun() -> run(Pic) end ), 40 | register(running_pic, Pid), 41 | Pid. 42 | 43 | stop_pic() -> 44 | running_pic ! {self(), exit}, 45 | P = receive 46 | V -> V 47 | end, 48 | P. 49 | 50 | create_pic200() -> 51 | #pic{ 52 | type = pic200, 53 | 54 | accumulator = <<0:8>>, 55 | register_file = pic_register:new(), 56 | callstack = [<<0:8>>,<<0:8>>], 57 | program = lists:duplicate(255,<<0:12>>), 58 | 59 | special_registers = orddict:from_list([]), % Flesh out later 60 | opcodes = fun pic200_opcodes/1, 61 | 62 | instruction_register = <<0:12>>, 63 | current_opcode = orddict:from_list([]), 64 | current_value = <<0:8>> 65 | }. 66 | 67 | load_program(P = #pic{register_file = R}, File) -> 68 | {ok, Bin} = file:read_file(File), 69 | Program = read_program(Bin,[]), 70 | E = 255 - length(Program), 71 | P#pic{ 72 | program = Program ++ lists:duplicate(E,<<0:12>>), 73 | register_file = pic_register:set(2,<<255:8>>,R) 74 | }. 75 | 76 | read_program(<<>>, Acc) -> lists:reverse(Acc); 77 | read_program(<>, Acc) -> 78 | read_program(Rest, [<>|Acc]); 79 | read_program(<<_/bitstring>>, Acc) -> lists:reverse(Acc). % Program code overflow 80 | 81 | run(P = #pic{}) -> 82 | show_info(P), 83 | receive 84 | q1 -> P0 = increment_program_counter(P), run( decode_instruction(P0) ); 85 | q2 -> run( fetch_from_register( P ) ); 86 | q3 -> run( execute_instruction( P ) ); 87 | q4 -> P0 = fetch_next_instruction(P), run( store_result( P0 ) ); 88 | exit -> ok; 89 | {Pid, exit} -> Pid ! P, ok; 90 | S -> io:format("Got signal ~w~n",[S]), run(P) 91 | end. 92 | 93 | show_info( P = #pic{ accumulator = <>, register_file = R, instruction_register = <> }) -> 94 | <> = pic_register:get(2,R), 95 | io:format("W=~8.2.0B PC=~8.2.0B I=~12.2.0B",[A,PC,I]), 96 | case orddict:find( name, P#pic.current_opcode ) of 97 | {ok, Name} -> io:format(" ~w~n", [Name]); 98 | error -> io:format("~n") 99 | end, 100 | 101 | lists:map( fun(V) -> <> = pic_register:get(V,R), io:format("~8.2.0B ",[Vl]) end, lists:seq(0,7) ), 102 | io:format("~n"), 103 | lists:map( fun(V) -> <> = pic_register:get(V,R), io:format("~8.2.0B ",[Vl]) end, lists:seq(8,15) ), 104 | io:format("~n"), 105 | lists:map( fun(V) -> <> = pic_register:get(V,R), io:format("~8.2.0B ",[Vl]) end, lists:seq(16,23) ), 106 | io:format("~n"), 107 | lists:map( fun(V) -> <> = pic_register:get(V,R), io:format("~8.2.0B ",[Vl]) end, lists:seq(24,31) ), 108 | io:format("~n"). 109 | 110 | 111 | decode_instruction( P = #pic{ instruction_register = Ir, opcodes = Oc } ) -> 112 | O = Oc(Ir), 113 | G = orddict:from_list(O(Ir)), 114 | P#pic{ current_opcode = G }. 115 | 116 | fetch_from_register( P = #pic{ current_opcode = O, register_file = R } ) -> 117 | P#pic{ current_value = case orddict:find(fetch,O) of 118 | {ok,Addr} -> pic_register:get(Addr, R); 119 | error -> <<0:8>> 120 | end}. 121 | 122 | % This method should also check for the status thingy 123 | execute_instruction( P = #pic{ current_opcode = O, current_value = <>, accumulator = <> } ) -> 124 | Val = case orddict:find(execute,O) of 125 | {ok, Func} -> Func(A,Cv); 126 | error -> Cv 127 | end, 128 | P#pic{ current_value = <> }. 129 | 130 | store_result( P = #pic{ current_opcode = O, current_value = Cv, register_file = R, accumulator = A } ) -> 131 | {R1,A1} = case orddict:find(store,O) of 132 | {ok, {source_register, Addr}} -> {pic_register:set(Addr,Cv,R),A}; 133 | {ok, accumulator } -> {R, Cv}; 134 | error -> {R,A} 135 | end, 136 | P0 = P#pic{register_file = R1, accumulator = A1}, 137 | P1 = case orddict:find(postproc,O) of 138 | {ok, Func} -> Func(P0,Cv); 139 | error -> P0 140 | end, 141 | P1. 142 | 143 | 144 | %% Byte oriented file_register operations 145 | pic200_opcodes(<< 7:6,_:6>>) -> fetch_store_opcode(addwf , fun (A,F) -> A + F end); % More status 146 | pic200_opcodes(<< 5:6,_:6>>) -> fetch_store_opcode(andwf , fun (A,F) -> A band F end); 147 | pic200_opcodes(<< 3:7,_:5>>) -> fetch_store_opcode(clrf , fun (_,_) -> 0 end); 148 | pic200_opcodes(<< 2:7,0:5>>) -> fetch_store_opcode(clrw , fun (_,_) -> 0 end); 149 | pic200_opcodes(<< 9:6,_:6>>) -> fetch_store_opcode(comf , fun (_,F) -> F bxor 255 end); 150 | pic200_opcodes(<< 3:6,_:6>>) -> fetch_store_opcode(decf , fun (_,F) -> F - 1 end); 151 | pic200_opcodes(<<11:6,_:6>>) -> skip_if_zero_opcode(decfsz, fun (_,F) -> F - 1 end); 152 | pic200_opcodes(<<10:6,_:6>>) -> fetch_store_opcode(incf , fun (_,F) -> F + 1 end); 153 | pic200_opcodes(<<15:6,_:6>>) -> skip_if_zero_opcode(incfsz, fun (_,F) -> F + 1 end); 154 | pic200_opcodes(<< 4:6,_:6>>) -> fetch_store_opcode(iorwf , fun (A,F) -> A bor F end); 155 | pic200_opcodes(<< 8:6,_:6>>) -> fetch_store_opcode(movf , fun (_,F) -> F end); 156 | pic200_opcodes(<< 1:7,_:5>>) -> movwf(); 157 | pic200_opcodes(<<0:12>>) -> fun(_) -> [{name, nop}] end; 158 | pic200_opcodes(<<13:6,_:6>>) -> fetch_store_opcode(rlf , fun (_,F) -> F bsl 1 end); % Carry bit 159 | pic200_opcodes(<<12:6,_:6>>) -> fetch_store_opcode(rrf , fun (_,F) -> F bsr 1 end); % Carry bit 160 | pic200_opcodes(<< 2:6,_:6>>) -> fetch_store_opcode(subwf , fun (A,F) -> F - A end); % More status 161 | pic200_opcodes(<<14:6,_:6>>) -> swapf(); 162 | pic200_opcodes(<< 6:6,_:6>>) -> fetch_store_opcode(xorwf , fun (A,F) -> F bxor A end); 163 | 164 | pic200_opcodes(<<6:4,_:8>>) -> bit_skip_if_zero(); 165 | 166 | pic200_opcodes(<<12:4,_:8>>) -> move_literal_opcode(); 167 | pic200_opcodes(<< 5:3,_:9>>) -> goto_opcode(). 168 | 169 | bit_skip_if_zero() -> 170 | fun(<>) -> 171 | Addr = 31 band Bin, 172 | Bit = 1 bsl (2#11100000 band Bin bsr 5), 173 | [ 174 | {name, btfsc}, 175 | {fetch, Addr}, 176 | {execute, fun(_,V) -> V band Bit end}, 177 | {postproc, fun skip_if_zero/2 } 178 | ] 179 | end. 180 | 181 | 182 | goto_opcode() -> 183 | fun(<>) -> 184 | Address = 2#11111111 band Bin, 185 | [ 186 | {name, goto}, 187 | {postproc, fun (P,_) -> 188 | P0 = set_program_counter(Address, P), 189 | P0#pic{ instruction_register = <<0:12>> } end} 190 | ] 191 | end. 192 | 193 | move_literal_opcode() -> 194 | fun(<>) -> 195 | Val = 2#11111111 band Bin, 196 | [ 197 | {name, movlw}, 198 | {store, accumulator}, 199 | {execute, fun (_,_) -> Val end} 200 | ] 201 | end. 202 | 203 | swapf() -> 204 | fun(<>) -> 205 | Whence = 32 band Bin bsr 5, 206 | Addr = 31 band Bin, 207 | [ 208 | {name, swapf}, 209 | {fetch, Addr}, 210 | {store, whence(Whence,Addr)}, 211 | {execute, fun(_,F) -> 212 | <> = <>, 213 | <> = <>, 214 | R end } 215 | ] 216 | end. 217 | 218 | movwf() -> 219 | fun(<>) -> 220 | Whence = 32 band Bin bsr 5, 221 | Addr = 31 band Bin, 222 | [ 223 | {name, movwf}, 224 | {execute, fun (A,_) -> A end}, 225 | {store, whence(Whence, Addr)} 226 | ] 227 | end. 228 | 229 | skip_if_zero_opcode(Name, ExecuteFun) -> 230 | fun(<>) -> 231 | Whence = 32 band Bin bsr 5, 232 | Addr = 31 band Bin, 233 | [ 234 | {name, Name}, 235 | {fetch, Addr}, 236 | {execute, ExecuteFun}, 237 | {store, whence(Whence, Addr)}, 238 | {postproc, fun skip_if_zero/2 } 239 | ] 240 | end. 241 | 242 | skip_if_zero(P,<<0:8>>) -> 243 | %P0 = increment_program_counter(P), 244 | P#pic{ instruction_register = <<0:12>> }; 245 | skip_if_zero(P,_) -> P. 246 | 247 | fetch_store_opcode(Name, ExecuteFun) -> 248 | fun(<>) -> 249 | Whence = 32 band Bin bsr 5, 250 | Addr = 31 band Bin, 251 | [ 252 | {name, Name}, 253 | {fetch, Addr}, 254 | {store, whence(Whence,Addr)}, 255 | {execute, ExecuteFun}, 256 | {status, [{z,fun z_status/2}] } 257 | ] 258 | end. 259 | 260 | z_status(P,_) -> P. 261 | 262 | set_program_counter(PVal, P = #pic{ register_file = Reg } ) -> 263 | P#pic{ register_file = pic_register:set(2, <>, Reg) }. 264 | 265 | increment_program_counter(P = #pic{ register_file = Reg } ) -> 266 | <> = pic_register:get(2, Reg), 267 | Inc = PC + 1, 268 | P#pic{ register_file = pic_register:set(2, <>, Reg) }. 269 | 270 | fetch_next_instruction(P = #pic{ register_file = Reg, program = Pr } ) -> 271 | <> = pic_register:get(2, Reg), 272 | Bin = lists:nth(PC+1,Pr), 273 | P#pic{ instruction_register = Bin }. 274 | 275 | whence(0,_) -> accumulator; 276 | whence(1,Addr) -> {source_register,Addr}. 277 | -------------------------------------------------------------------------------- /pic_emulator.erl: -------------------------------------------------------------------------------- 1 | -module(pic_emulator). 2 | %-compile(export_all). %% REPLACE 3 | 4 | -export([new/0, print/1, load_program/2, 5 | run_program/1,run_program/2, cycle/3]). 6 | 7 | -record(pic200,{ 8 | accumulator = <<0>>, % 8 bits 9 | register = pic_register:new(), % 32 bytes 10 | callstack = [<<0>>,<<0>>], % 2 bytes 11 | program, % 256 x 12 bits 12 | 13 | option, % 8 bits 14 | trisgpio, % 8 bits (but only 4bits used) 15 | 16 | instruction_register = <<0:12>>, 17 | current_opcode, 18 | current_value 19 | }). 20 | 21 | -record(opcode,{ 22 | opcode = nop, 23 | address = 16#1F, % Last register is safe default 24 | whence = accumulator, 25 | constant = 0, 26 | bit = 0 27 | }). 28 | 29 | new() -> 30 | #pic200{ 31 | register = pic_register:new(), 32 | program = lists:duplicate(255,<<0:12>>) 33 | }. 34 | 35 | load_program(P, File) -> 36 | {ok, Bin} = file:read_file(File), 37 | Program = read_program(Bin,[]), 38 | E = 255 - length(Program), 39 | P#pic200{ program = Program ++ lists:duplicate(E,<<0:12>>) }. 40 | 41 | read_program(<<>>, Acc) -> lists:reverse(Acc); 42 | read_program(<>, Acc) -> 43 | read_program(Rest, [<>|Acc]). 44 | %read_program(<<_/bitstring>>, Acc) -> lists:reverse(Acc). 45 | 46 | %% Description of one cycle (4 clocks) 47 | % Fetch cycle Execution cycle 48 | % Q1. inc_pc fetch_instr -> ir2 49 | % Q2. - read_memory (from reg) 50 | % Q3. - execute_instr 51 | % Q4. fetch_pc -> ir1 write_result (to reg) 52 | 53 | cycle(P,q1,fetch) -> increment_program_counter(P); 54 | cycle(P,q4,fetch) -> 55 | <> = pic_register:get( 2, P#pic200.register ), 56 | NextInstr = pic_register:get( PC, P#pic200.program ), 57 | P#pic200{ instruction_register = NextInstr }; 58 | cycle(P,_,fetch) -> P; 59 | 60 | cycle(P,q1,execute) -> 61 | O = unpack_opcode(P#pic200{ instruction_register } ), 62 | P#pic200{ current_opcode = O }; 63 | cycle(P,q2,execute) -> 64 | O = P#pic200.current_opcode, 65 | <> = pic_register:get( O#opcode.address, P#pic200.register ), 66 | P#pic200{ current_value = FVal }; 67 | cycle(P,q3,execute) -> P; 68 | cycle(P,q4,execute) -> P. 69 | 70 | run_program(P , 0 ) -> P; 71 | run_program(P, Steps ) -> 72 | PN = run_program(P), 73 | run_program(PN, Steps - 1). 74 | 75 | run_program(P = #pic200{ register = R, program = Program } ) -> 76 | <> = pic_register:get( 2, R ), 77 | OpcodeBin = pic_register:get( PC, Program ), 78 | execute_opcode(OpcodeBin, P). 79 | 80 | execute_opcode(OpcodeBin, P = #pic200{accumulator = <>}) -> 81 | #opcode{opcode = Opcode, address = Addr, whence = Whence, constant = K, bit = B} 82 | = unpack_opcode(OpcodeBin), 83 | valid_addr(Addr), 84 | <> = pic_register:get( Addr, P#pic200.register ), 85 | Func = orddict:fetch(Opcode,opcode_table()), 86 | {Store,Res,ResP,Status} = Func(increment_program_counter(P), Acc, FVal, K, B), % Should we really increment here 87 | 88 | case store(Store,Whence) of 89 | accumulator -> ResP#pic200{ accumulator = <> }; 90 | source_register -> ResP#pic200{ register = pic_register:set(Addr, <>, ResP#pic200.register) }; 91 | no -> ResP 92 | end. 93 | 94 | chain(Obj, []) -> Obj; 95 | chain(Obj, [F|Funcs]) -> chain(F(Obj), Funcs). 96 | 97 | print(#pic200{ 98 | accumulator = <>, register = R, callstack = [<>,<>], 99 | program = P 100 | }) -> 101 | io:format("Accumulator: ~8.2B ~3.10B Callstack: ~8.2B ~8.2b~n",[A,A,C1,C2]), 102 | io:format("Registers:~n"), 103 | pic_register:print(R), 104 | io:format("Program:~n"), 105 | program_print(P), 106 | ok. 107 | 108 | program_print(P) -> 109 | program_print(lists:sublist(P,1,9),0). 110 | 111 | program_print([],_) -> io:nl(); 112 | program_print([HeadBin|R],A) -> 113 | <> = HeadBin, 114 | OT = opcode_term(HeadBin), 115 | io:format(" ~2.10B : ~12.2.0B ~5.10B [~6w]~n",[A,H,H,OT]), program_print(R,A+1). 116 | 117 | opcode_term(OpcodeBin) -> 118 | #opcode{opcode = Opcode} = unpack_opcode(OpcodeBin), 119 | Opcode. 120 | 121 | valid_addr(_) -> ok. % Should check in future 122 | 123 | store(store,W) -> W; 124 | store(S,_) -> S. 125 | 126 | %% fun(#pic200, Acc, RVal, K, bits) -> {store, res, P} 127 | opcode_table() -> orddict:from_list([ 128 | {nop, fun(P,_,_,_,_) -> { no, 0 , P, []} end }, 129 | 130 | {clrwdt,fun(P,_,_,_,_) -> { no, 0 , P, [to,pd]} end }, 131 | {sleep, fun(P,_,_,_,_) -> { no, 0 , P, [to,pd]} end }, 132 | 133 | {clrw, fun(P,_,_,_,_) -> { accumulator, 0 , P, [z]} end }, 134 | {clrf, fun(P,_,_,_,_) -> {source_register, 0 , P, [z]} end }, 135 | {movwf, fun(P,A,_,_,_) -> {source_register, A , P, []} end }, 136 | 137 | {subwf, fun(P,A,F,_,_) -> {store, F-A , P, [z,c,dc]} end }, 138 | {decf, fun(P,_,F,_,_) -> {store, F-1 , P, [z]} end }, 139 | {iorwf, fun(P,A,F,_,_) -> {store, A bor F , P, [z]} end }, 140 | {andwf, fun(P,A,F,_,_) -> {store, A band F , P, [z]} end }, 141 | {xorwf, fun(P,A,F,_,_) -> {store, A bxor F , P, [z]} end }, 142 | {addwf, fun(P,A,F,_,_) -> {store, A+F , P, [z,c,dc]} end }, 143 | {movf, fun(P,_,F,_,_) -> {store, F , P, [z]} end }, 144 | {comf, fun(P,_,F,_,_) -> {store, bnot F , P, [z]} end }, 145 | {incf, fun(P,_,F,_,_) -> {store, F+1 , P, [z]} end }, 146 | {decfsz,fun(P,_,1,_,_) -> {accumulator, 0 , skip(P), []}; 147 | (P,_,F,_,_) -> {store, F-1 , P, []} end }, 148 | {rrf, fun(P,_,F,_,_) -> {store, F bsr 1 , P, [c]} end }, % TODO: carry 149 | {rlf, fun(P,_,F,_,_) -> {store, F bsl 1 , P, [c]} end }, % TODO: carry 150 | {swapf, fun(P,_,F,_,_) -> {store, swapf(F) , P, []} end }, 151 | {incfsz,fun(P,_,255,_,_) -> {accumulator, 0 , skip(P), []}; 152 | (P,_,F,_,_) -> {store, F+1 , P, []} end }, 153 | 154 | {bcf, fun(P,_,F,_,B) -> {source_register,F bxor (F band (1 bsl (B-1))), P, []} end}, 155 | {bsf, fun(P,_,F,_,B) -> {source_register,F bor (1 bsl (B-1)), P, []} end }, 156 | {btfsc, fun(P,_,F,_,B) -> {no, F band (1 bsl (B-1)), P, []} end }, % TODO: check condition 157 | {btfss, fun(P,_,F,_,B) -> {no, F band (1 bsl (B-1)), P, []} end }, % TODO: check condition 158 | 159 | {retlw, fun(P,_,_,K,_) -> {accumulator, K, return(P), []} end }, 160 | {call, fun(P,_,_,K,_) -> {no , 0, call(P,K), []} end }, 161 | {goto, fun(P,_,_,K,_) -> {no , 0, goto(P,K), []} end }, 162 | {movlw, fun(P,_,_,K,_) -> {accumulator, K, P, []} end }, 163 | {iorlw, fun(P,A,_,K,_) -> {accumulator, K bor A, P, [z]} end }, 164 | {andlw, fun(P,A,_,K,_) -> {accumulator, K band A, P, [z]} end }, 165 | {xorlw, fun(P,A,_,K,_) -> {accumulator, K bxor A, P, []} end } 166 | ]). 167 | 168 | clear_insreg(P) -> P#pic200{ instruction_register = <<0:12>> }. 169 | 170 | skip(P = #pic200{ register = R}) -> 171 | <> = pic_register:get(2,R), 172 | clear_insreg(P#pic200{ register = pic_register:set(2,O+1,R) }). 173 | 174 | goto(P = #pic200{ register = R }, K) -> 175 | Pn = case bit_size(K) of 176 | 8 -> P#pic200{ register = pic_register:set(2, K, R) }; 177 | 9 -> <<_:1,RealK:8>> = K, 178 | P#pic200{ register = pic_register:set(2, RealK, R) } 179 | end, 180 | clear_insreg(Pn). 181 | 182 | call(P = #pic200{ register = R, callstack = C }, K) -> 183 | goto( P#pic200{ callstack = [pic_register:get(2,R),hd(C)] }, K ). 184 | 185 | return(P = #pic200{ callstack = [A|R] } ) -> 186 | goto( P#pic200{ callstack = R }, A ). 187 | 188 | swapf(V) -> 189 | <> = <>, 190 | <> = <>, 191 | R. 192 | 193 | increment_program_counter(P = #pic200{ register = Reg } ) -> 194 | <> = pic_register:get(2, Reg), 195 | Inc = PC + 1, 196 | P#pic200{ register = pic_register:set(2, <>, Reg) }. 197 | 198 | whence(0) -> accumulator; 199 | whence(1) -> source_register. 200 | 201 | unpack_opcode(<<0:12>>) -> #opcode{opcode = nop }; 202 | unpack_opcode(<<2:12>>) -> #opcode{opcode = option }; 203 | unpack_opcode(<<3:12>>) -> #opcode{opcode = sleep }; 204 | unpack_opcode(<<4:12>>) -> #opcode{opcode = clrwdt }; 205 | unpack_opcode(<<1:10,A:2>>) -> #opcode{opcode = tris, address = A}; 206 | 207 | unpack_opcode(<<1:7,A:5>>) -> #opcode{opcode = movwf, address = A}; 208 | unpack_opcode(<<2:7,_:5>>) -> #opcode{opcode = clrw }; 209 | unpack_opcode(<<3:7,A:5>>) -> #opcode{opcode = clrf, address = A}; 210 | 211 | unpack_opcode(<< 2:6,W:1,A:5>>) -> #opcode{opcode = subwf, address = A, whence = whence(W)}; 212 | unpack_opcode(<< 3:6,W:1,A:5>>) -> #opcode{opcode = decf, address = A, whence = whence(W)}; 213 | unpack_opcode(<< 4:6,W:1,A:5>>) -> #opcode{opcode = iorwf, address = A, whence = whence(W)}; 214 | unpack_opcode(<< 5:6,W:1,A:5>>) -> #opcode{opcode = andwf, address = A, whence = whence(W)}; 215 | unpack_opcode(<< 6:6,W:1,A:5>>) -> #opcode{opcode = xorwf, address = A, whence = whence(W)}; 216 | unpack_opcode(<< 7:6,W:1,A:5>>) -> #opcode{opcode = addwf, address = A, whence = whence(W)}; 217 | unpack_opcode(<< 8:6,W:1,A:5>>) -> #opcode{opcode = movf, address = A, whence = whence(W)}; 218 | unpack_opcode(<< 9:6,W:1,A:5>>) -> #opcode{opcode = comf, address = A, whence = whence(W)}; 219 | unpack_opcode(<<10:6,W:1,A:5>>) -> #opcode{opcode = incf, address = A, whence = whence(W)}; 220 | unpack_opcode(<<11:6,W:1,A:5>>) -> #opcode{opcode = decfsz, address = A, whence = whence(W)}; 221 | unpack_opcode(<<12:6,W:1,A:5>>) -> #opcode{opcode = rrf, address = A, whence = whence(W)}; 222 | unpack_opcode(<<13:6,W:1,A:5>>) -> #opcode{opcode = rlf, address = A, whence = whence(W)}; 223 | unpack_opcode(<<14:6,W:1,A:5>>) -> #opcode{opcode = swapf, address = A, whence = whence(W)}; 224 | unpack_opcode(<<15:6,W:1,A:5>>) -> #opcode{opcode = incfsz, address = A, whence = whence(W)}; 225 | 226 | unpack_opcode(<<4:4,B:3,A:5>>) -> #opcode{opcode = bcf, address = A, bit = B}; 227 | unpack_opcode(<<5:4,B:3,A:5>>) -> #opcode{opcode = bsf, address = A, bit = B}; 228 | unpack_opcode(<<6:4,B:3,A:5>>) -> #opcode{opcode = btfsc, address = A, bit = B}; 229 | unpack_opcode(<<7:4,B:3,A:5>>) -> #opcode{opcode = btfss, address = A, bit = B}; 230 | 231 | unpack_opcode(<< 8:4, K:8>>) -> #opcode{opcode = retlw, constant = K}; 232 | unpack_opcode(<< 9:4, K:8>>) -> #opcode{opcode = call, constant = K}; 233 | unpack_opcode(<< 5:3, K:9>>) -> #opcode{opcode = goto, constant = K}; 234 | unpack_opcode(<<12:4, K:8>>) -> #opcode{opcode = movlw, constant = K}; 235 | unpack_opcode(<<13:4, K:8>>) -> #opcode{opcode = iorlw, constant = K}; 236 | unpack_opcode(<<14:4, K:8>>) -> #opcode{opcode = andlw, constant = K}; 237 | unpack_opcode(<<15:4, K:8>>) -> #opcode{opcode = xorlw, constant = K}. 238 | 239 | --------------------------------------------------------------------------------