From 55b2d15cd05482226af7dc05736dbbcb9f147378 Mon Sep 17 00:00:00 2001 From: Ryan Pendleton Date: Wed, 6 Jul 2022 19:18:09 -0600 Subject: [PATCH] remove an extra break (closes #57) --- docs/index.html | 185 ++++++++++++++++++++++----------------------- docs/src/lc3-win.c | 1 - docs/src/lc3.c | 1 - index.lit | 1 - 4 files changed, 92 insertions(+), 96 deletions(-) diff --git a/docs/index.html b/docs/index.html index 8a134c0..2866dc9 100644 --- a/docs/index.html +++ b/docs/index.html @@ -72,7 +72,7 @@

Memory

#define MEMORY_MAX (1 << 16)
 uint16_t memory[MEMORY_MAX];  /* 65536 locations */
 
-

Used by 1 2 3 4

+

Used by 1 2 3 4

Registers

@@ -104,7 +104,7 @@

Registers

R_COUNT }; -

Used by 1 2 3 4

+

Used by 1 2 3 4

Just like the memory, we will store the registers in an array:

@@ -114,7 +114,7 @@

Registers

Register Storage
uint16_t reg[R_COUNT];
 
-

Used by 1 2 3 4

+

Used by 1 2 3 4

Instruction Set

@@ -148,7 +148,7 @@

Instruction Set

OP_TRAP /* execute trap */ }; -

Used by 1 2 3 4

+

Used by 1 2 3 4

Note: The Intel x86 architecture has hundreds of instructions, while others such as ARM and LC-3 have very few. Small instruction sets are referred to as RISCs while larger ones are called CISCs. Larger instruction sets typically do not provide any fundamentally new possibilities, but they often make it more convenient to write assembly for. A single instruction in CISC might take the place of several in RISC. However, they tend to be more complex and expensive for engineers to design and manufacture. This and other tradeoffs cause the designs to come in and out of style.

@@ -169,7 +169,7 @@

Condition Flags

FL_NEG = 1 << 2, /* N */ }; -

Used by 1 2 3 4

+

Used by 1 2 3 4

Note: (The << symbol is called the left bitshift operator. (n << k) shifts the bits of n to the left k places. Thus 1 << 2 will equal 4. Read that link if you are not familiar. It will be important.)

@@ -179,7 +179,7 @@

Condition Flags

/lc3.c -
@{Includes}
+
@{Includes}
 
 @{Registers}
 @{Opcodes}
@@ -269,7 +269,7 @@ 

Procedure

int main(int argc, const char* argv[])
 {
     @{Load Arguments}
-    @{Setup}
+    @{Setup}
 
     /* since exactly one condition flag should be set at any given time, set the Z flag */
     reg[R_COND] = FL_ZRO;
@@ -307,28 +307,28 @@ 

Procedure

@{JSR} break; case OP_LD: - @{LD} + @{LD} break; case OP_LDI: @{LDI} break; case OP_LDR: - @{LDR} + @{LDR} break; case OP_LEA: - @{LEA} + @{LEA} break; case OP_ST: - @{ST} + @{ST} break; case OP_STI: - @{STI} + @{STI} break; case OP_STR: - @{STR} + @{STR} break; case OP_TRAP: - @{TRAP} + @{TRAP} break; case OP_RES: case OP_RTI: @@ -337,10 +337,10 @@

Procedure

break; } } - @{Shutdown} + @{Shutdown} }
-

Used by 1 2

+

Used by 1 2

While we are at the main loop let’s handle command line input to make our program usable. @@ -365,7 +365,7 @@

Procedure

} }
-

Used by 1 2 3

+

Used by 1 2 3

@@ -425,7 +425,7 @@

ADD

return x; }
-

Used by 1 2 3 4

+

Used by 1 2 3 4

Note: If you are interested in exactly how negative numbers can be represented in binary, you can read about Two’s Complement. However, this is not essential. You can just copy the code above and use it whenever the specification says to sign extend numbers.

@@ -455,7 +455,7 @@

ADD

} } -

Used by 1 2 3 4

+

Used by 1 2 3 4

Now we are ready to write the code for the ADD case:

@@ -674,7 +674,6 @@

Jump Register

uint16_t r1 = (instr >> 6) & 0x7; reg[R_PC] = reg[r1]; /* JSRR */ } - break; }

Used by 1

@@ -684,7 +683,7 @@

Load

-LD +LD
{
     uint16_t r0 = (instr >> 9) & 0x7;
     uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
@@ -699,7 +698,7 @@ 

Load Register

-LDR +LDR
{
     uint16_t r0 = (instr >> 9) & 0x7;
     uint16_t r1 = (instr >> 6) & 0x7;
@@ -715,7 +714,7 @@ 

Load Effective Address

-LEA +LEA
{
     uint16_t r0 = (instr >> 9) & 0x7;
     uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
@@ -730,7 +729,7 @@ 

Store

-ST +ST
{
     uint16_t r0 = (instr >> 9) & 0x7;
     uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
@@ -744,7 +743,7 @@ 

Store Indirect

-STI +STI
{
     uint16_t r0 = (instr >> 9) & 0x7;
     uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
@@ -758,7 +757,7 @@ 

Store Register

-STR +STR
{
     uint16_t r0 = (instr >> 9) & 0x7;
     uint16_t r1 = (instr >> 6) & 0x7;
@@ -782,7 +781,7 @@ 

7. Trap Routines

-TRAP Codes +TRAP Codes
enum
 {
     TRAP_GETC = 0x20,  /* get character from keyboard, not echoed onto the terminal */
@@ -793,7 +792,7 @@ 

7. Trap Routines

TRAP_HALT = 0x25 /* halt the program */ };
-

Used by 1 2 3 4

+

Used by 1 2 3 4

You may be wondering why the trap codes are not included in the instructions. @@ -817,32 +816,32 @@

7. Trap Routines

-TRAP +TRAP
reg[R_R7] = reg[R_PC];
 
 switch (instr & 0xFF)
 {
     case TRAP_GETC:
-        @{TRAP GETC}
+        @{TRAP GETC}
         break;
     case TRAP_OUT:
-        @{TRAP OUT}
+        @{TRAP OUT}
         break;
     case TRAP_PUTS:
-        @{TRAP PUTS}
+        @{TRAP PUTS}
         break;
     case TRAP_IN:
-        @{TRAP IN}
+        @{TRAP IN}
         break;
     case TRAP_PUTSP:
-        @{TRAP PUTSP}
+        @{TRAP PUTSP}
         break;
     case TRAP_HALT:
-        @{TRAP HALT}
+        @{TRAP HALT}
         break;
 }
 
-

Used by 1 2

+

Used by 1 2

As with instructions, I will show you how to implement a single trap routine and leave the rest to you.

@@ -862,7 +861,7 @@

PUTS

-TRAP PUTS +TRAP PUTS
{
     /* one char per word */
     uint16_t* c = memory + reg[R_R0];
@@ -874,7 +873,7 @@ 

PUTS

fflush(stdout); }
-

Used by 1

+

Used by 1

That’s all for this routine. The trap routines are pretty straightforward if you are familiar with C. Go back to the specification and implement the others now. As with the instructions, the full code can be found at the end of the tutorial.

@@ -888,30 +887,30 @@

8. Trap Routine Cheat Sheet

-TRAP GETC +TRAP GETC
/* read a single ASCII char */
 reg[R_R0] = (uint16_t)getchar();
 update_flags(R_R0);
 
-

Used by 1

+

Used by 1

Output Character

-TRAP OUT +TRAP OUT
putc((char)reg[R_R0], stdout);
 fflush(stdout);
 
-

Used by 1

+

Used by 1

Prompt for Input Character

-TRAP IN +TRAP IN
{
     printf("Enter a character: ");
     char c = getchar();
@@ -921,14 +920,14 @@ 

8. Trap Routine Cheat Sheet

update_flags(R_R0); }
-

Used by 1

+

Used by 1

Output String

-TRAP PUTSP +TRAP PUTSP
{
     /* one char per byte (two bytes per word)
        here we need to swap back to
@@ -945,19 +944,19 @@ 

8. Trap Routine Cheat Sheet

fflush(stdout); }
-

Used by 1

+

Used by 1

Halt Program

-TRAP HALT +TRAP HALT
puts("HALT");
 fflush(stdout);
 running = 0;
 
-

Used by 1

+

Used by 1

@@ -973,7 +972,7 @@

9. Loading Programs

-Read Image File +Read Image File
void read_image_file(FILE* file)
 {
     /* the origin tells us where in memory to place the image */
@@ -994,20 +993,20 @@ 

9. Loading Programs

} }
-

Used by 1 2 3 4

+

Used by 1 2 3 4

Notice that swap16 is called on each loaded value. LC-3 programs are big-endian, but most modern computers are little-endian. So, we need to swap each uint16 that is loaded. (If you happen to be using an obscure computer, like an old PPC Mac, then do not swap.)

-Swap +Swap
uint16_t swap16(uint16_t x)
 {
     return (x << 8) | (x >> 8);
 }
 
-

Used by 1 2 3 4

+

Used by 1 2 3 4

Note: Endianness refers to how bytes of an integer are interpreted. In little-endian, the first byte is the least significant digit, and in big-endian, it is reversed. As far as I know, the decision is mostly arbitrary. Different companies made different decisions, so now we are left with varying implementations. You do not need to know anything else about endianness for this project.

@@ -1016,7 +1015,7 @@

9. Loading Programs

-Read Image +Read Image
int read_image(const char* image_path)
 {
     FILE* file = fopen(image_path, "rb");
@@ -1026,7 +1025,7 @@ 

9. Loading Programs

return 1; }
-

Used by 1 2 3 4

+

Used by 1 2 3 4

@@ -1042,21 +1041,21 @@

10. Memory Mapped Registers

-Memory Mapped Registers +Memory Mapped Registers
enum
 {
     MR_KBSR = 0xFE00, /* keyboard status */
     MR_KBDR = 0xFE02  /* keyboard data */
 };
 
-

Used by 1 2 3 4

+

Used by 1 2 3 4

Memory mapped registers make memory access a bit more complicated. We can’t read and write to the memory array directly, but must instead call setter and getter functions. When memory is read from KBSR, the getter will check the keyboard and update both memory locations.

-Memory Access +Memory Access
void mem_write(uint16_t address, uint16_t val)
 {
     memory[address] = val;
@@ -1079,7 +1078,7 @@ 

10. Memory Mapped Registers

return memory[address]; }
-

Used by 1 2 3 4

+

Used by 1 2 3 4

That completes the last component of the VM! Provided that you implemented the rest of the trap routines and instructions, you are almost ready to try it out!

@@ -1088,22 +1087,22 @@

10. Memory Mapped Registers

-/lc3.c += -
@{Memory Mapped Registers}
-@{TRAP Codes}
+/lc3.c +=
+
@{Memory Mapped Registers}
+@{TRAP Codes}
 
 @{Memory Storage}
 @{Register Storage}
 
 @{Sign Extend}
-@{Swap}
+@{Swap}
 @{Update Flags}
-@{Read Image File}
-@{Read Image}
-@{Check Key}
-@{Memory Access}
-@{Input Buffering}
-@{Handle Interrupt}
+@{Read Image File}
+@{Read Image}
+@{Check Key}
+@{Memory Access}
+@{Input Buffering}
+@{Handle Interrupt}
 
 @{Main Loop}
 
@@ -1122,7 +1121,7 @@

11. Platform Specifics (Unix)

-Check Key +Check Key
uint16_t check_key()
 {
     fd_set readfds;
@@ -1135,14 +1134,14 @@ 

11. Platform Specifics (Unix)

return select(1, &readfds, NULL, NULL, &timeout) != 0; }
-

Used by 1 2

+

Used by 1 2

This is Unix specific code for setting up terminal input.

-Input Buffering +Input Buffering
struct termios original_tio;
 
 void disable_input_buffering()
@@ -1158,14 +1157,14 @@ 

11. Platform Specifics (Unix)

tcsetattr(STDIN_FILENO, TCSANOW, &original_tio); }
-

Used by 1 2

+

Used by 1 2

-Handle Interrupt +Handle Interrupt
void handle_interrupt(int signal)
 {
     restore_input_buffering();
@@ -1173,18 +1172,18 @@ 

11. Platform Specifics (Unix)

exit(-2); }
-

Used by 1 2 3 4

+

Used by 1 2 3 4

-Setup +Setup
signal(SIGINT, handle_interrupt);
 disable_input_buffering();
 
-

Used by 1 2 3

+

Used by 1 2 3

When the program is interrupted, we want to restore the terminal @@ -1192,17 +1191,17 @@

11. Platform Specifics (Unix)

-Shutdown +Shutdown
restore_input_buffering();
 
-

Used by 1 2 3

+

Used by 1 2 3

-Includes +Includes
#include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
@@ -1217,7 +1216,7 @@ 

11. Platform Specifics (Unix)

#include <sys/termios.h> #include <sys/mman.h>
-

Used by 1 2

+

Used by 1 2

@@ -1232,20 +1231,20 @@

12. Platform Specifics (Windows)

-Check Key Windows +Check Key Windows
uint16_t check_key()
 {
     return WaitForSingleObject(hStdin, 1000) == WAIT_OBJECT_0 && _kbhit();
 }
 
-

Used by 1 2

+

Used by 1 2

-Input Buffering Windows +Input Buffering Windows
DWORD fdwMode, fdwOldMode;
 
 void disable_input_buffering()
@@ -1265,14 +1264,14 @@ 

12. Platform Specifics (Windows)

SetConsoleMode(hStdin, fdwOldMode); }
-

Used by 1 2

+

Used by 1 2

-Windows Setup +Windows Setup
signal(SIGINT, handle_interrupt);
 disable_input_buffering();
 
@@ -1284,7 +1283,7 @@

12. Platform Specifics (Windows)

-Windows Shutdown +Windows Shutdown
restore_input_buffering();
 
@@ -1294,7 +1293,7 @@

12. Platform Specifics (Windows)

-Windows Includes +Windows Includes
#include <stdint.h> // uint16_t
 #include <stdio.h>  // FILE
 #include <signal.h> // SIGINT
@@ -1304,7 +1303,7 @@ 

12. Platform Specifics (Windows)

HANDLE hStdin = INVALID_HANDLE_VALUE;
-

Used by 1 2

+

Used by 1 2

@@ -1361,7 +1360,7 @@

14. Alternate C++ Technique

-Instruction C++ +Instruction C++
template <unsigned op>
 void ins(uint16_t instr)
 {
@@ -1447,20 +1446,20 @@ 

14. Alternate C++ Technique

if (0x0080 & opbit) { mem_write(base_plus_off, reg[r0]); } // STR if (0x8000 & opbit) // TRAP { - @{TRAP} + @{TRAP} } //if (0x0100 & opbit) { } // RTI if (0x4666 & opbit) { update_flags(r0); } }
-

Used by 1 2

+

Used by 1 2

-Op Table +Op Table
static void (*op_table[16])(uint16_t) = {
     ins<0>, ins<1>, ins<2>, ins<3>,
     ins<4>, ins<5>, ins<6>, ins<7>,
@@ -1468,7 +1467,7 @@ 

14. Alternate C++ Technique

ins<12>, NULL, ins<14>, ins<15> };
-

Used by 1 2

+

Used by 1 2

Note: I learned about this technique from Bisqwit’s NES emulator. diff --git a/docs/src/lc3-win.c b/docs/src/lc3-win.c index 8cfeda6..30e9c6b 100644 --- a/docs/src/lc3-win.c +++ b/docs/src/lc3-win.c @@ -289,7 +289,6 @@ int main(int argc, const char* argv[]) uint16_t r1 = (instr >> 6) & 0x7; reg[R_PC] = reg[r1]; /* JSRR */ } - break; } break; case OP_LD: diff --git a/docs/src/lc3.c b/docs/src/lc3.c index 80ffba4..b9e7f9c 100644 --- a/docs/src/lc3.c +++ b/docs/src/lc3.c @@ -296,7 +296,6 @@ int main(int argc, const char* argv[]) uint16_t r1 = (instr >> 6) & 0x7; reg[R_PC] = reg[r1]; /* JSRR */ } - break; } break; case OP_LD: diff --git a/index.lit b/index.lit index 40c731e..106fa4f 100644 --- a/index.lit +++ b/index.lit @@ -552,7 +552,6 @@ abort(); uint16_t r1 = (instr >> 6) & 0x7; reg[R_PC] = reg[r1]; /* JSRR */ } - break; } ---